Signal on Kernel parameter change - c

I want to use parameter inside a kernel (3.x) module:
static char param = 0xff;
module_param(param, ushort, S_IRUGO | S_IWUGO);
MODULE_PARM_DESC(param, "a parameter");
Is there any possibility to detect changes on this parameter? Is there a signal which can use to call a service routine?
Best regards
Alex
Additional information to module_param(name,type,perm):
possible values for type:
bool
invbool
charp
int
long
short
uint
ulong
ushort
defines for perm at linux/stat.h
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
Here a full code example:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex");
MODULE_DESCRIPTION("test module.");
MODULE_VERSION("0.1");
static int param = 1;
module_param(param, int, S_IRUGO|S_IWUGO);
static int __init mod_init(void){
printk(KERN_INFO "param %d\n", param);
return 0;
}
static void __exit mod_exit(void){
printk(KERN_INFO "Goodbye!\n");
}
module_init(mod_init);
module_exit(mod_exit);
The compiler returns this error I don't understand why:
make -C /lib/modules/3.19.0-47-generic/build/ M=/tmp/gt modules
make[1]: change to »/usr/src/linux-headers-3.19.0-47-generic«
CC [M] /tmp/gt/ebb.o
In file included from include/linux/thread_info.h:11:0,
from ./arch/x86/include/asm/preempt.h:6,
from include/linux/preempt.h:18,
from include/linux/spinlock.h:50,
from include/linux/seqlock.h:35,
from include/linux/time.h:5,
from include/linux/stat.h:18,
from include/linux/module.h:10,
from /tmp/gt/ebb.c:2:
include/linux/bug.h:33:45: error: negative width in bit-field ‘<anonymous>’
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
^
include/linux/kernel.h:830:3: note: in expansion of macro ‘BUILD_BUG_ON_ZERO’
BUILD_BUG_ON_ZERO((perms) & 2) + \
^
include/linux/moduleparam.h:223:31: note: in expansion of macro ‘VERIFY_OCTAL_PERMISSIONS’
= { __param_str_##name, ops, VERIFY_OCTAL_PERMISSIONS(perm), \
^
include/linux/moduleparam.h:166:2: note: in expansion of macro ‘__module_param_call’
__module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)
^
include/linux/moduleparam.h:146:2: note: in expansion of macro ‘module_param_cb’
module_param_cb(name, &param_ops_##type, &value, perm); \
^
include/linux/moduleparam.h:125:2: note: in expansion of macro ‘module_param_named’
module_param_named(name, name, type, perm)
^
/tmp/gt/ebb.c:11:1: note: in expansion of macro ‘module_param’
module_param(param, int, S_IRUGO|S_IWUGO);
^
make[2]: *** [/tmp/gt/ebb.o] Fehler 1
make[1]: *** [_module_/tmp/gt] Fehler 2
make[1]: leaving »/usr/src/linux-headers-3.19.0-47-generic«
make: *** [all] Fehler 2
and here the Makefile
obj-m+=ebb.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
regarding to the answer from #Tsyvarev
Writing rights are only allowed to root. So here the change:
module_param(param, int, S_IRUGO|S_IWUSR);
Now you can find the parameter under /sys/module//parameters/ and you can change the parameter like this:
sudo echo 2 > /sys/module/ebb/parameters/param
next step regarding to #Tsyvarev
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex");
MODULE_DESCRIPTION("test module.");
MODULE_VERSION("0.1");
// int (*set)(const char *val, const struct kernel_param *kp);
// int (*get)(char *buffer, const struct kernel_param *kp);
int my_param_set_ushort(const char *val, const struct kernel_param *kp)
{
unsigned short* pvalue = kp->arg; // Pointer to actual parameter variable.
int res = param_set_ushort(val, kp); // Use helper for write variable
printk(KERN_INFO "setter talks\n");
if( res==0 )
{
// Here you may execute additional actions when you write parameter.
printk(KERN_INFO "set param %d\n", *pvalue);
}
return res;
}
const struct kernel_param_ops my_param_ops_ushort =
{
.set = &my_param_set_ushort, // Use our setter ...
.get = &param_get_ushort, // .. and standard getter
};
unsigned short param = 0xff;
module_param_cb(param, /*filename*/
&my_param_ops_ushort, /*operations*/
&param, /* pointer to variable, contained parameter's value */
S_IRUGO | S_IWUSR /*permissions on file*/
);
static int __init mod_init(void){
printk(KERN_INFO "param %d\n", param);
return 0;
}
static void __exit mod_exit(void){
printk(KERN_INFO "Goodbye! (%d)\n",param);
}
module_init(mod_init);
module_exit(mod_exit);
as root I give this commands:
# insmod par.ko
# echo 146 > /sys/module/par/parameters/param
# rmmod par
and the kernel log /var/log/kernel.log says:
Jan 23 14:27:37 alex-XMG kernel: [ 8332.492912] param 255
Jan 23 14:27:39 alex-XMG kernel: [ 8334.520044] setter talks
Jan 23 14:27:39 alex-XMG kernel: [ 8334.520052] set param 146
Jan 23 14:27:40 alex-XMG kernel: [ 8335.804338] Goodbye! (146)
Works like a charme!

Generic way for create kernel module parameter is using macro module_param_cb:
/**
* module_param_cb - general callback for a module/cmdline parameter
* #name: a valid C identifier which is the parameter name.
* #ops: the set & get operations for this parameter.
* #perm: visibility in sysfs.
*
* The ops can have NULL set or get functions.
*/
#define module_param_cb(name, ops, arg, perm)
Parameter ops is a pointer to structure struct kernel_param_ops, which contains operations for given parameter. Functions called when parameter is written and read have followed definition in this structure:
int (*set)(const char *val, const struct kernel_param *kp);
int (*get)(char *buffer, const struct kernel_param *kp);
Here char* parameter is a NULL-terminated string, which is written to/read from the sysfs file, denoted given parameter. And kp is a pointer to parameter descriptor, where the most interested field is .arg: it is a 3rd argument to macro module_param_cb call. Using this field, setter and getter can be implemented per-type of the module parameter, that is having 5 int parameters for module doesn't require to write setters and getters for each of them.
Moreover, getters and setters for standard parameter types are already implemented, and they are actually used when you call module_param macro. So, if you want to add some functionality for the parameter's setter, you may reuse existed helpers:
int my_param_set_ushort(const char *val, const struct kernel_param *kp)
{
unsigned short* pvalue = kp->arg; // Pointer to actual parameter variable.
int res = param_set_ushort(val, kp); // Use helper for write variable
if(!res)
{
// Here you may execute additional actions when you write parameter.
printk(KERN_INFO "set param %d\n", *pvalue);
}
return res;
}
const struct kernel_param_ops my_param_ops_ushort =
{
.set = &my_param_set_ushort, // Use our setter ...
.get = &param_get_ushort, // .. and standard getter
};
// Usage
unsigned short param = 0xff;
module_param_cb(param, /*filename*/
&my_param_ops_ushort, /*operations*/
&param, /* pointer to variable, contained parameter's value */
S_IRUGO | S_IWUSR /*permissions on file*/
);
Having module parameter's writable by non-priveledged user normally is not good for security reasons. And kernel macros, which create module parameters, check that. That is why you have cryptic error in your module parameter's definition. Note, that in the example above S_IWUSR is used instead of S_IWUGO.

If you make your module parameter writable you can change that parameter via sysfs. Once loaded, find your module under /sys, then find the module parameter and verify that it is writable (by root). Try writing to it and see if it changes.
There are a few ways to detect changes to this variable. You can use a kernel thread as one mechanism.
A better approach might be to use a sysfs or procfs entry. Those have read/write handlers which are called upon reads and writes.
See here for an sysfs-tutorial

Related

Convention of using #includes for the header files for using a system call provided by linux kernel into my custom system call

I am trying to design a system call of my own. And as per need, I am considering using a system call already provided by linux kernel. Below is the program :
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/cpu.h>
#include <linux/sched/isolation.h>
//#include <../tools/include/nolibc/sys.h>
SYSCALL_DEFINE1(my_test, int , cpuid){
umode_t m=0;
int fd=sys_open("abc.txt", O_RDWR, m);
sys_write(fd, "I AM ABHISHEK\n", 14);
// remove_cpu(cpuid);
// //printk("This message is printed from a system call by Abhishek Ghosh");
// housekeeping_isolcpus_setup("domain,managed_irq,0");
// housekeeping_nohz_full_setup("0");
// add_cpu(cpuid);
return 0;
}
I am trying to achieve something as the following as given above. Open a file, in the context of the process which calls it. fd shall index into the open-file table of the process, which calls my system call. And then using that fd, I try to write a string to it.
arch/x86/entry/syscalls/syscall_64.tbl, mentions that sys_open is the 2nd syscall in the table. Also, sys_write is the 1st syscall in the table.
include/linux/syscalls.h has
asmlinkage long sys_open(const char __user *filename,
int flags, umode_t mode); //line 1089
and
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
size_t count); //line 500
In spite of including #include <linux/syscalls.h> while compiling the kernel I get the following errors:
my_syscalls/my_test.c: In function ‘__do_sys_my_test’:
my_syscalls/my_test.c:9:12: error: implicit declaration of function ‘sys_open’; did you mean ‘seq_open’? [-Werror=implicit-function-declaration]
9 | int fd=sys_open("abc.txt", O_RDWR);
| ^~~~~~~~
| seq_open
my_syscalls/my_test.c:10:5: error: implicit declaration of function ‘sys_write’; did you mean ‘ksys_write’? [-Werror=implicit-function-declaration]
10 | sys_write(fd, "I AM ABHISHEK\n", 14);
| ^~~~~~~~~
| ksys_write
My custom system call definition is written in a directory under the folder linux-6.0.19, and I have added the required other modification.
The definition of the required sys_open and sys_write are present in tools/include/nolibc/sys.h, but when I include that header, after making modifications to the line :
#include <stdarg.h> ---------> #include <linux/stdarg.h>
The situation becomes even worse. Many constants like O_RDWR etc get redefined, and many issues come concerning incomplete structure definition and conflicting structure definition...
I am a beginner in linux kernel programming. Please can you guide me, as to how to use the #includes in this case?
I mean instead of having the function declaration in include/linux/syscalls.h, why the compiler says implicit declaraction ?
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/cpu.h>
#include <linux/sched/isolation.h>
// #include <../tools/include/nolibc/sys.h>
extern asmlinkage long sys_write(unsigned int fd, const char __user *buf,
size_t count);
extern asmlinkage long sys_open(const char __user *filename,
int flags, umode_t mode);
SYSCALL_DEFINE1(my_test, int , cpuid){
umode_t mode=0;
int fd=sys_open("abc.txt", O_RDWR,mode);
sys_write(fd, "I AM ABHISHEK\n", 14);
// remove_cpu(cpuid);
// //printk("This message is printed from a system call by Abhishek Ghosh");
// housekeeping_isolcpus_setup("domain,managed_irq,0");
// housekeeping_nohz_full_setup("0");
// add_cpu(cpuid);
return 0;
}
Using extern declaration in the beginning, resolves the implicit declaration error, but now there is a link time issue, undefined reference.
abhishek#abhishek:~/OS_Proj/linux-6.0.19_full_fledged_backup$ make -j16
DESCEND objtool
CALL scripts/atomic/check-atomics.sh
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC my_syscalls/my_test.o
AR my_syscalls/built-in.a
CHK kernel/kheaders_data.tar.xz
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
AR init/built-in.a
LD vmlinux.o
MODPOST vmlinux.symvers
MODINFO modules.builtin.modinfo
GEN modules.builtin
LD .tmp_vmlinux.kallsyms1
ld: my_syscalls/my_test.o: in function `__do_sys_my_test':
/home/abhishek/OS_Proj/linux-6.0.19_full_fledged_backup/my_syscalls/my_test.c:15: undefined reference to `sys_open'
ld: /home/abhishek/OS_Proj/linux-6.0.19_full_fledged_backup/my_syscalls/my_test.c:16: undefined reference to `sys_write'
ld: /home/abhishek/OS_Proj/linux-6.0.19_full_fledged_backup/my_syscalls/my_test.c:15: undefined reference to `sys_open'
ld: /home/abhishek/OS_Proj/linux-6.0.19_full_fledged_backup/my_syscalls/my_test.c:16: undefined reference to `sys_write'
make: *** [Makefile:1169: vmlinux] Error 1
abhishek#abhishek:~/OS_Proj/linux-6.0.19_full_fledged_backup$

Error: definition of ‘int __atomic_compare_exchange(params1)’ ambiguates built-in declaration ‘bool __atomic_compare_exchange(params2)’

I am trying to build Berkley Db 4.8.30 on Ubuntu 20.0.4 LTS
This is what I did so far:
wget http://download.oracle.com/berkeley-db/db-4.8.30.zip
unzip db-4.8.30.zip
cd db-4.8.30
cd build_unix/
../dist/configure --prefix=/usr/local --enable-cxx
make
make install
During the compilation, I get the error messsage:
error: definition of ‘int __atomic_compare_exchange(db_atomic_t*,
atomic_value_t, atomic_value_t)’ ambiguates built-in declaration ‘bool
__atomic_compare_exchange(long unsigned int, volatile void*, void*, void*, int, int)’
The compilation flags are set correctly for my platform. Here are the relevant section of code:
atomic.h (truncated)
#if defined(DB_WIN32)
typedef DWORD atomic_value_t;
#else
typedef int32_t atomic_value_t;
#endif
/*
* Windows CE has strange issues using the Interlocked APIs with variables
* stored in shared memory. It seems like the page needs to have been written
* prior to the API working as expected. Work around this by allocating an
* additional 32-bit value that can be harmlessly written for each value
* used in Interlocked instructions.
*/
#if defined(DB_WINCE)
typedef struct {
volatile atomic_value_t value;
volatile atomic_value_t dummy;
} db_atomic_t;
#else
typedef struct {
volatile atomic_value_t value;
} db_atomic_t;
#endif
#define atomic_compare_exchange(env, p, o, n) \
__atomic_compare_exchange((p), (o), (n))
/*
* x86/gcc Compare exchange for shared latches. i486+
* Returns 1 for success, 0 for failure
*
* GCC 4.1+ has an equivalent __sync_bool_compare_and_swap() as well as
* __sync_val_compare_and_swap() which returns the value read from *dest
* http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
* which configure could be changed to use.
*/
static inline int __atomic_compare_exchange(
db_atomic_t *p, atomic_value_t oldval, atomic_value_t newval)
{
atomic_value_t was;
if (p->value != oldval) /* check without expensive cache line locking */
return 0;
__asm__ __volatile__("lock; cmpxchgl %1, (%2);"
:"=a"(was)
:"r"(newval), "r"(p), "a"(oldval)
:"memory", "cc");
return (was == oldval);
}
I think I need to set a pragma to ignore the type safety check ... but not too sure. How do I resolve this compilation error?
The function
bool __atomic_compare_exchange(long unsigned int, volatile void*, void*, void*, int, int)
is a built-in GCC compiler function for an atomic operation. See here for details:
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
The function
int __atomic_compare_exchange(db_atomic_t*, atomic_value_t, atomic_value_t)
seems to be a function of Berkley Db 4.8.30 and this creates a conflict. No two functions may have the same name.
The following link is a link to a patch (including patch instructions) that you can apply to solve the problem:
https://gist.github.com/danieldk/5700533

errors with C code (reads GPIO input off a Raspberry Pi Zero W), compiles on Jessie but doesn't compile on Buster Lite

I am very new to C (had one semester class like 5 years ago). To summarize my problem, I have code written in C that compiles on Raspbian Jessie, but not Buster Lite.
I'm using a Raspberry Pi Zero W + ADCs to input an analog signal, digitize it, and transmit it wirelessly and the code I'm having issues with takes the data from the GPIOs and stores it in a buffer that I can read from. I previously was running Raspbian Jessie on my pi and I had NO issues compiling the code (no warnings, no errors). It was correctly reading data from the ADCs and storing it in the buffer. When I installed Raspbian Buster Lite on my Pi, and have been getting multiple errors/warnings when compiling. (I'm not concerned about the warnings/notes, know how to fix them, just the errors. partly included because I don't understand why they appeared on Buster but not on Jessie)
make -C /lib/modules/4.19.97+/build M=/home/pi modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.97+'
CC [M] /home/pi/adc.o
/home/pi/adc.c:132:9: warning: useless storage class specifier in empty declaration
};
^
/home/pi/adc.c: In function ‘readScope’:
/home/pi/adc.c:176:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
struct timespec ts_start,ts_stop;
^~~~~~
/home/pi/adc.c:218:29: error: assignment to ‘unsigned char *’ from incompatible pointer type ‘struct DataStruct *’ [-Werror=incompatible-pointer-types]
ScopeBufferStart=&dataStruct;
^
/home/pi/adc.c: At top level:
/home/pi/adc.c:225:9: warning: "/*" within comment [-Wcomment]
/*
In file included from ./include/linux/printk.h:7,
from ./include/linux/kernel.h:14,
from /home/pi/adc.c:1:
/home/pi/adc.c: In function ‘init_module’:
./include/linux/kern_levels.h:5:18: warning: too many arguments for format [-Wformat-extra-args]
#define KERN_SOH "\001" /* ASCII Start Of Header */
^~~~~~
./include/linux/kern_levels.h:9:20: note: in expansion of macro ‘KERN_SOH’
#define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
^~~~~~~~
/home/pi/adc.c:248:24: note: in expansion of macro ‘KERN_ALERT’
printk(KERN_ALERT,"Failed to map the physical GPIO registers into the virtual memory space.\n");
^~~~~~~~~~
/home/pi/adc.c:283:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
struct bcm2835_peripheral *p=&myclock;
^~~~~~
/home/pi/adc.c:290:13: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
int speed_id = 6; //1 for to start with 19Mhz or 6 to start with 500 MHz
^~~
/home/pi/adc.c: In function ‘device_read’:
/home/pi/adc.c:359:35: warning: comparison of distinct pointer types lacks a cast
while (length && buf_p<ScopeBufferStop) {
^
In file included from /home/pi/adc.c:4:
./arch/arm/include/asm/uaccess.h:387:33: error: incompatible types when initializing type ‘char’ using type ‘struct DataStruct’
__typeof__(*(ptr)) __pu_val = (x); \
^
./arch/arm/include/asm/uaccess.h:404:2: note: in expansion of macro ‘__put_user_switch’
__put_user_switch((x), (ptr), __pu_err, __put_user_check); \
^~~~~~~~~~~~~~~~~
/home/pi/adc.c:361:23: note: in expansion of macro ‘put_user’
if(0!=put_user(*(buf_p++), buffer++))
^~~~~~~~
At top level:
/home/pi/adc.c:111:26: warning: ‘ScopeBuffer_Ptr’ defined but not used [-Wunused-variable]
static uint32_t *ScopeBuffer_Ptr;
^~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
make[2]: *** [scripts/Makefile.build:310: /home/pi/adc.o] Error 1
make[1]: *** [Makefile:1522: _module_/home/pi] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-4.19.97+'
make: *** [Makefile:4: all] Error 2
What I've tried:
One of the first errors I got (code updated since then, used to have buf_ declared as const char *buf_p;) is that I'm using incompatible pointer types:
/home/pi/adc.c:262:19: error: assignment to ‘unsigned char *’ from incompatible pointer type ‘struct DataStruct *’ [-Werror=incompatible-pointer-types]
ScopeBufferStart=&dataStruct;
When I fix this (by redefining the variable buf_p as a struct DataStruct), I get a new error in the device_read function that I haven't been able to fix:
In file included from /home/pi/adc.c:13:
./arch/arm/include/asm/uaccess.h:387:33: error: incompatible types when initializing type ‘char’ using type ‘struct DataStruct’
__typeof__(*(ptr)) __pu_val = (x); \
I also noticed that the output from the Makefile on Buster Lite I get
make -C /lib/modules/4.19.97+/build M=/home/pi modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.97+'
...
...
make[1]: Leaving directory '/usr/src/linux-headers-4.19.97+'
while on Jessie I get
make -C /lib/modules/`uname -r`/build M=$PWD
make[1]: Entering directory '/usr/src/linux-source-4.4.50+'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/linux-source-4.4.50+'
Here is my code...
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include <linux/io.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/fcntl.h> /*Helps fix O_ACCMODE*/
#include <linux/sched.h> /*Helps fix TASK_UNINTERRUPTIBLE */
#include <linux/fs.h> /*Helps fix the struct intializer */
int __init init_module(void);
void __exit cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
#define SUCCESS 0
#define DEVICE_NAME "stdin"// Dev name
#define BUF_LEN 80//Max length of device message
//---------------------------------------------------------------------------------------------------------
//Things for the GPIO Port
#define BCM2708_PERI_BASE 0x20000000
//if you're using a RPi3, PERIBASE value should be changed to 0x3F000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) // GPIO controller
// depends on the RPi
#define INP_GPIO(g) *(gpio.addr + ((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio.addr + ((g)/10)) |= (1<<(((g)%10)*3)) //001
//alternative function
#define SET_GPIO_ALT(g,a) *(gpio.addr + (((g)/10))) |= (((a)<=3?(a) + 4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio.addr + 7) // sets bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio.addr + 10) // clears bits which are 1 ignores bits which are 0
#define GPIO_READ(g) *(gpio.addr + 13) &= (1<<(g))
//GPIO Clock
#define CLOCK_BASE (BCM2708_PERI_BASE + 0x00101000)
#define GZ_CLK_BUSY (1 << 7)
//---------------------------------------------------------------------------------------------------------
//How many samples to capture
#define SAMPLE_SIZE 10000 // 2x2500 pts in one line
#define REPEAT_SIZE 10 // 10 captures
//Define GPIO Pins
//ADC 1
#define BIT0_ADC1 16
#define BIT1_ADC1 17
#define BIT2_ADC1 18
#define BIT3_ADC1 19
#define BIT4_ADC1 20
#define BIT5_ADC1 22
#define BIT6_ADC1 25
#define BIT7_ADC1 26
#define BIT8_ADC1 27
//ADC 2
#define BIT0_ADC2 7
#define BIT1_ADC2 8
#define BIT2_ADC2 9
#define BIT3_ADC2 10
#define BIT4_ADC2 11
#define BIT5_ADC2 12
#define BIT6_ADC2 13
#define BIT7_ADC2 14
#define BIT8_ADC2 15
// Pulser
#define Puls_ON 23
#define Puls_OFF 24
#define PPWWMM 6
#define MY_NOP(__N) __asm ("nop"); // or sth like "MOV R0,R0"
//---------------------------------------------------------------------------------------------------------
// IO Acces
struct bcm2835_peripheral {
unsigned long addr_p;
int mem_fd;
void *map;
volatile unsigned int *addr;
};
static int map_peripheral(struct bcm2835_peripheral *p);
static void unmap_peripheral(struct bcm2835_peripheral *p);
static void readScope(void);
static int Major; /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open?
* Used to prevent multiple access to device */
static char msg[BUF_LEN]; /* The msg the device will give when asked */
static char *msg_Ptr;
static uint32_t *ScopeBuffer_Ptr;
// static unsigned char *buf_p;
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
//--------------------------------------------------------------------------------------------------------
static struct bcm2835_peripheral myclock = {CLOCK_BASE};
static struct bcm2835_peripheral gpio = {GPIO_BASE};
static struct DataStruct{
uint32_t Buffer[REPEAT_SIZE*SAMPLE_SIZE];
uint32_t time;
};
struct DataStruct dataStruct;
struct DataStruct *buf_p;
static unsigned char *ScopeBufferStart;
static unsigned char *ScopeBufferStop;
//---------------------------------------------------------------------------------------------------------
static int map_peripheral(struct bcm2835_peripheral *p)
{
p->addr=(uint32_t *)ioremap(GPIO_BASE, 41*4); //41 GPIO register with 32 bit (4*8)
return 0;
}
static void unmap_peripheral(struct bcm2835_peripheral *p) {
iounmap(p->addr);//unmap the address
}
//---------------------------------------------------------------------------------------------------------
static void readScope(){
int counter=0;
int counterline = 0;
int limit = 0;
int Pon=0;
int Poff=0;
//int Fail=0;
OUT_GPIO(Puls_ON);
OUT_GPIO(Puls_OFF);
GPIO_SET = 1 << Puls_ON;
GPIO_CLR = 1 << Puls_OFF;
msleep(10);
//disable IRQ
local_irq_disable();
local_fiq_disable();
struct timespec ts_start,ts_stop;
//Start time
set_current_state(TASK_UNINTERRUPTIBLE);
getnstimeofday(&ts_start);
while(counterline<REPEAT_SIZE){
Pon = 0;
Poff = 0;
limit = (counterline+1)*SAMPLE_SIZE;
while(counter<(limit) ){
dataStruct.Buffer[counter++]= *(gpio.addr + 13);
}
// to avoid freezes
// msleep(0.5);
counterline++;
}
//Stop time
getnstimeofday(&ts_stop);
INP_GPIO(Puls_ON);
INP_GPIO(Puls_OFF);
set_current_state(TASK_INTERRUPTIBLE);
//enable IRQ
local_fiq_enable();
local_irq_enable();
//save the time difference
dataStruct.time=timespec_to_ns(&ts_stop)-timespec_to_ns(&ts_start);//ns resolution
// buf_p=&dataStruct;//cound maybe removed
buf_p=&dataStruct;//cound maybe removed
ScopeBufferStart=&dataStruct;
ScopeBufferStop=ScopeBufferStart+sizeof(struct DataStruct);
}
//---------------------------------------------------------------------------------------------------------
/*
/*
* This function is called when the module is loaded
*/
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk(KERN_ALERT "Registering char device failed with %d\n", Major);
return Major;
}
printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
printk(KERN_INFO "the driver, create a dev file with\n");
printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
printk(KERN_INFO "the device file.\n");
printk(KERN_INFO "Remove the device file and module when done.\n");
//Map GPIO
if(map_peripheral(&gpio) == -1)
{
printk(KERN_ALERT,"Failed to map the physical GPIO registers into the virtual memory space.\n");
return -1;
}
//Define Scope pins
// ADC1
INP_GPIO(BIT0_ADC1);
INP_GPIO(BIT1_ADC1);
INP_GPIO(BIT2_ADC1);
INP_GPIO(BIT3_ADC1);
INP_GPIO(BIT4_ADC1);
INP_GPIO(BIT5_ADC1);
INP_GPIO(BIT6_ADC1);
INP_GPIO(BIT7_ADC1);
INP_GPIO(BIT8_ADC1);
// ADC2
INP_GPIO(BIT0_ADC2);
INP_GPIO(BIT1_ADC2);
INP_GPIO(BIT2_ADC2);
INP_GPIO(BIT3_ADC2);
INP_GPIO(BIT4_ADC2);
INP_GPIO(BIT5_ADC2);
INP_GPIO(BIT6_ADC2);
INP_GPIO(BIT7_ADC2);
INP_GPIO(BIT8_ADC2);
// Setting pins for pulser
OUT_GPIO(Puls_ON);
OUT_GPIO(Puls_OFF);
GPIO_CLR = 1 << Puls_ON; // set pulser at 0
GPIO_SET = 1 << Puls_OFF; // set damper at 1
//Set a clock signal on Pin 4
struct bcm2835_peripheral *p=&myclock;
p->addr=(uint32_t *)ioremap(CLOCK_BASE, 41*4);
INP_GPIO(4);
SET_GPIO_ALT(4,0);
// Preparing the clock
int speed_id = 6; //1 for to start with 19Mhz or 6 to start with 500 MHz
*(myclock.addr+28)=0x5A000000 | speed_id; //Turn off the clock
while (*(myclock.addr+28) & GZ_CLK_BUSY) {}; //Wait until clock is no longer busy (BUSY flag)
*(myclock.addr+29)= 0x5A000000 | (0x29 << 12) | 0;//Set divider //divide by 50 (0x32) -- ideally 41 (29) to fall on 12MHz clock
*(myclock.addr+28)=0x5A000010 | speed_id;//Turn clock on
return SUCCESS;
}
//---------------------------------------------------------------------------------------------------------
/*
* This function is called when the module is unloaded
*/
void cleanup_module(void)
{
unregister_chrdev(Major, DEVICE_NAME);
unmap_peripheral(&gpio);
unmap_peripheral(&myclock);
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process tries to open the device file, like
* "cat /dev/mycharfile"
*/
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open)
return -EBUSY;
Device_Open++;
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
msg_Ptr = msg;
readScope();//Read n Samples into memory
try_module_get(THIS_MODULE);
return SUCCESS;
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process closes the device file.
*/
static int device_release(struct inode *inode, struct file *file)
{
Device_Open--; /* We're now ready for our next caller */
module_put(THIS_MODULE);
return 0;
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process, which already opened the dev file, attempts to
* read from it.
*/
static ssize_t device_read(struct file *filp,
char *buffer,
size_t length,
loff_t * offset)
{
// Number of bytes actually written to the buffer
int bytes_read = 0;
if (*msg_Ptr == 0)
return 0;
//Check that we do not overfill the buffer
while (length && buf_p<ScopeBufferStop) {
if(0!=put_user(*(buf_p++), buffer++))
printk(KERN_INFO "Problem with copy\n");
length--;
bytes_read++;
}
return bytes_read;
}
//---------------------------------------------------------------------------------------------------------
/*
* Called when a process writes to dev file: echo "hi" > /dev/hello
*/
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
return -EINVAL;
}
Thank you
So I think I have an answer. I realized I need to compile the linux kernel module on Buster before I can compile my adc code. On the Jessie disk image version I was using, I realized the linux kernel module had already been compiled, I think that's why on Jessie it's able to enter the linux source directory to compile while on Buster it enters the linux headers directory. Thank you

Linux Kernel Module/IOCTL: inappropriate ioctl for device

I am in the process of writing a Linux Kernel Module (LKM) serving as a pseudo-driver - I am unable to figure out how to make IOCTL calls between the LKM (wait.c) and the user-level program (user.c).
The magic number for the device driver is 0xBF - the LKM does not communicate with a physical block/char device, it is simply an exercise. From what I can tell, the IOCTL call to KERN_IOCTL_CREATE_EVENT is not formatted properly & the magic number is incorrect.
The IOCTL call that I am attempting to use is:
#include <sys/ioctl.h>
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 1, int)
int main(){
int ret;
int fd;
fd = open("/dev/wait", 0);
if(fd < 0){
return -1;
}
ret = ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0);
Error:
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
The user-mode application can open/close a file descriptor pointing to the device: /dev/wait but the case/switch statement isn't accepting the IOCTL call. Any suggestions?
Here is the output of # uname -a
Linux vagrant-ubuntu-trusty-64 3.13.11.11+ #1 SMP Mon Dec 1 20:50:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
wait.c
#include <linux/miscdevice.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include "wait.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tyler Fisher <tyler#tylerfisher.org>");
MODULE_DESCRIPTION("In-kernel wait queue");
static unsigned long event_table_size = 50;
module_param(event_table_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH));
MODULE_PARM_DESC(event_table_size, "Size of event table (i.e. how many processes can be blocking)");
/* IOCTL function headers */
static int wait_open(struct inode *, struct file *);
static int wait_close(struct inode *, struct file *);
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
/* other function headers */
static long event_open(int event_id);
/* file operations */
static struct file_operations wait_fops = {
.owner = THIS_MODULE,
.open = wait_open,
.release = wait_close,
.llseek = noop_llseek,
.unlocked_ioctl = wait_ioctl
};
/* device handler */
static struct miscdevice wait_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = WAIT_DEVICE_NAME,
.fops = &wait_fops
};
/* open wait device */
static int wait_open(struct inode *inode, struct file *file){
dev_t node = iminor(inode);
if(MINOR(node) != WAIT_DEVICE_MINOR){
return -ENODEV;
}
return 0;
}
static int wait_close(struct inode *inode, struct file *file){
return 0;
}
static long wait_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long sub_cmd){
switch(cmd){
case KERN_IOCTL_CREATE_EVENT:
printk(KERN_INFO "[wait device]: create event %lu\n", sub_cmd);
return event_open(sub_cmd);
default:
return -ENOENT;
}
}
static long event_open(int id){
return 0;
}
static long __init wait_init(void){
if(misc_register(&wait_misc_device) < 0){
printk(KERN_ERR "[wait device] failed to register device\n");
return -1;
}
printk(KERN_INFO "[wait device] has been registered\n");
return 0;
}
static void __exit wait_exit(void){
misc_deregister(&wait_misc_device);
printk(KERN_INFO "[wait device] has been unregistered\n");
}
module_init(wait_init);
module_exit(wait_exit);
wait.h
#include <linux/ioctl.h>
#define WAIT_DEVICE_NAME "wait"
#define WAIT_DEVICE_MAGIC 0xBF
#define WAIT_DEVICE_MAJOR 200
#define WAIT_DEVICE_MINOR 0
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define MAX_WAITING 5
The test program for the IOCTL calls:
user.c
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#define WAIT_DEVICE_MAGIC 0xBF
#define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define KERN_IOCTL_DESTROY_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x02, int)
#define KERN_IOCTL_LOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x03, int)
#define KERN_IOCTL_UNLOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x04, int)
int main(){
int fd;
if(fd = open("/dev/wait", O_RDWR) < 0){
perror("failed to open /dev/wait");
return -1;
}
/* test IOCTL: event creation */
if(ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0) < 0){
perror("[fail]: KERN_IOCTL_CREATE_EVENT");
return -1;
}
return 0;
}
Makefile
obj-m += wait.o
CFLAGS_wait.o += -DDEBUG
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
In order to test out the LKM - clear dmesg, compile & execute user.c w/GCC:
# dmesg -c > /dev/null 2>&1
# make
# rmmod wait.ko
# insmod wait.ko
# gcc user.c -o user && ./user
The amount of debugging errors is embarassing. I feel bad for sharing this - and realize that this may cause the issue to be closed/downvoted into oblivion.
# sh test.sh
[+] cleared dmesg
make -C /lib/modules/3.13.11.11+/build M=/home/vagrant/PROG40000-kernel-synchronization modules
make[1]: Entering directory `/home/vagrant/ubuntu-trusty'
CC [M] /home/vagrant/PROG40000-kernel-synchronization/wait.o
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: initialization from incompatible pointer type [enabled by default]
};
^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: (near initialization for ‘wait_fops.unlocked_ioctl’) [enabled by default]
In file included from include/linux/moduleparam.h:4:0,
from /home/vagrant/PROG40000-kernel-synchronization/wait.c:11:
/home/vagrant/PROG40000-kernel-synchronization/wait.c: In function ‘__inittest’:
include/linux/init.h:297:4: warning: return from incompatible pointer type [enabled by default]
{ return initfn; } \
^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:167:1: note: in expansion of macro ‘module_init’
module_init(wait_init);
^
Building modules, stage 2.
MODPOST 1 modules
CC /home/vagrant/PROG40000-kernel-synchronization/wait.mod.o
LD [M] /home/vagrant/PROG40000-kernel-synchronization/wait.ko
make[1]: Leaving directory `/home/vagrant/ubuntu-trusty'
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[-/dmesg--]
[+] compiled user-mode program
-----
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[+] executed user-mode program
-----
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[13112.893049] SOMEONE DARE READ FROM ME!?
[13112.893057] [wait device] invalid magic number: 0:0:191
[13112.893535] [wait device] invalid magic number: 0:0:191
[-/dmesg--]
Okay. So. Here's the solution.
In Linux kernel 2.6.x the declaration for _ioctl calls changed from
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
To:
static long wait_ioctl(struct file *, unsigned int, unsigned long);
The fix is thus:
...
static long wait_ioctl(struct file *, unsigned int, unsigned long);
...
static long wait_ioctl(struct file *file, unsigned int cmd, unsigned long sub_cmd){
if(_IOC_TYPE(cmd) != WAIT_DEVICE_MAGIC){
printk(KERN_INFO "[wait device] invalid magic number: %u:%u:%u", _IOC_TYPE(cmd), cmd, WAIT_DEVICE_MAGIC);
return -ENOTTY;
}
....
.compat_ioctl
Also make sure to implement this file_operation if you are making 32-bit calls to a 64-bit kernel.
The symptom is that your ioctl handler is never run.

C89 - Initialising struct with flexible char array and prototypes

I'm new to C and had some questions around struct instantiation. I have two files :
Index.c : which instantiates a new Server struct
server/Server.c which defines the Server struct, the new_Server() constructor and the kill_Server() deconstructor
Content of Index.c :
/* Standard libraries */
#include <stdio.h>
#include <string.h>
/* Project's Custom classes */
#include "./server/Server.c"
/* Configuration value */
#define HOST "127.0.0.1"
#define PORT 80
int main (void) {
unsigned short port = PORT;
unsigned char host[255] = HOST;
Server* server = new_Server(port, host);
return 0;
}
Content of server/Server.c :
#include <stdlib.h>
/* HOST_NAME_MAX will have to be changed into getconf HOST_NAME_MAX */
#define HOST_NAME_MAX 255
typedef struct {
unsigned short port;
unsigned char host[HOST_NAME_MAX];
} Server;
Server* new_Server (unsigned short port, unsigned char host[]) {
Server* server = malloc(sizeof(Server));
server->port = port;
server->host = host;
return server;
}
void kill_Server (Server* server) {
free(server);
}
When I compile the program, I get the following output :
In file included from src/index.c:6:
src/./server/Server.c:11:9: warning: no previous prototype for function 'new_Server' [-Wmissing-prototypes]
Server* new_Server (unsigned short port, unsigned char host[]) {
^
src/./server/Server.c:14:15: error: array type 'unsigned char [255]' is not assignable
server->host = host;
~~~~~~~~~~~~ ^
src/./server/Server.c:18:6: warning: no previous prototype for function 'kill_Server' [-Wmissing-prototypes]
void kill_Server (Server* server) {
^
2 warnings and 1 error generated.
(I just left out the "server" variable not used warning.)
So here are my questions :
Why am I getting the "missing prototype" warning since I actually did specify the output and method argument types ?
How can I initialise a struct with it's "host" key being an array of chars ?
Is what I'm doing efficient ? Steps :
configure values in a #define
create the corresponding variables
passing them as constructor parameters
initialise a struct instance
assign the value to it's associated key
I read that to get the maximum hostname size you should do "getconf HOST_NAME_MAX". In the shell it works of course and I get 255, but I'd like to store the value in a variable in my C program.
How can I achieve this ?
I'm compiling with the following GCC flags :
gcc -g -O0 -Wall -Wextra -std=c89 -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Wold-style-definition -Wdeclaration-after-statement
I know this is (unnecessarily) strict, but I'd like to learn C the hard way and really understand it's ins-and-outs. And I think that stuff like warnings are great for this.
EDIT: I did already read this question How to initialize a structure with flexible array member, but I didn't really understand the answer. It also leaves out the issue about method prototypes.
Well, you should avoid using #include to include a .c file. The .c files are supposed to be compiled separately (with common definitions in a .h file that both include), and then you link the compiled objects together.
The two warnings is bogus, there is no code problem. You could suppress the warning by adding a prototype (just copy line 11 but put a ; on the end). Same for the second one
The error: server->host = host; is illegal. Arrays are second-class citizens in C, you cannot copy them using the = operator. You need to either go memcpy(&server->host, &host, sizeof server->host);, or strcpy(server->host, host);.
I think it'd be overkill to have your C program execute getconf and see what it got; instead, look up what the max value is that getconf will ever give back to you.

Resources