I'm trying to build a Ruby C Extension for Raspberry Pi using some existing C code. The code relies on the bcm2835-1.35 library for some functions. Problem is that when I try to run the a ruby program using on the RPi, I get the following:
ruby: symbol lookup error: /usr/lib/ruby/site_ruby/2.1.0/armv6l-linux-eabihf/dht_test.so: undefined symbol: bcm2835_gpio_fsel
Here's what the code looks like, this is all in one directory (~/Ruby/dht_test)
extconf.rb
# Loads mkmf which is used to make makefiles for Ruby extensions
require 'mkmf'
# Load Libraries
LIBDIR = RbConfig::CONFIG['libdir']
INCLUDEDIR = RbConfig::CONFIG['includedir']
HEADER_DIRS = [
# First search /opt/local for macports
'/opt/local/include',
# Then search /usr/local for people that installed from source
'/usr/local/include',
# Check the ruby install locations
INCLUDEDIR,
# Finally fall back to /usr
'/usr/include',
]
LIB_DIRS = [
# Then search /usr/local for people that installed from source
'/usr/local/lib',
# Check the ruby install locations
LIBDIR,
# Finally fall back to /usr
'/usr/lib',
]
unless find_header('bcm2835.h')
abort 'bcm2835 is missing'
end
dir_config('bcm2835', HEADER_DIRS, LIB_DIRS) # Tried with the line commented out, doesn't make any difference
# Give it a name
extension_name = 'dht_test'
# The destination
dir_config(extension_name)
# Do the work
create_makefile(extension_name)
dhtreader.h
/* for usleep */
//#define _BSD_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bcm2835.h>
#include <unistd.h>
int readDHT(int type, int pin, float *temp_p, float *hum_p);
dhtreader.c
#include "dhtreader.h"
int readDHT(int type, int pin, float *temp_p, float *hum_p)
{
int counter = 0;
int laststate = HIGH;
int i = 0;
int j = 0;
int checksum = 0;
#ifdef DEBUG
int bitidx = 0;
int bits[250];
#endif
int data[100];
// Set GPIO pin to output
bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_write(pin, HIGH);
usleep(500000); // 500 ms
bcm2835_gpio_write(pin, LOW);
usleep(20000);
bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
ect....
dht_test.c
// Include the Ruby headers and goodies
#include "ruby.h"
#include "dhtreader.h"
// Defining a space for information and references about the module to be stored internally
VALUE DhtTest = Qnil;
// Prototype for the initialization method - Ruby calls this, not you
void Init_dht_test();
// Prototype for our method 'test1' - methods are prefixed by 'method_' here
VALUE method_test1(VALUE self);
VALUE method_test2(VALUE self);
// The initialization method for this module
void Init_dht_test() {
DhtTest = rb_define_module("DhtTest");
rb_define_method(DhtTest, "test1", method_test1, 0);
rb_define_method(DhtTest, "test2", method_test2, 0);
}
// Our 'test1' method.. it simply returns a value of '10' for now.
VALUE method_test1(VALUE self) {
int x = 10;
return INT2NUM(x);
}
VALUE method_test2(VALUE self){
float temp;
float hum;
readDHT(22, 4, &temp, &hum);
printf("Temperture: %f, Humidity %f", temp, hum);
return temp;
}
Finally
I try to run the following irb, the first function test1 works fine, the second function which requires the bcm2835 fails with the previously stated error
require 'dht_test'
include DhtTest
puts test1
puts test2
I'm new to this stuff and I'm sure I'm making some egregious mistakes, but I'm pretty much stuck and would appreciate any help I can get.
Figured it out, I needed to add the following to my extconf.rb
unless have_library('bcm2835') && append_library($libs, 'bcm2835')
abort "Unable to appended Library bcm2835!"
end
Also this is needed to compile in c99
$CFLAGS << ' -std=c99' // yes you need the space
Related
I realized that I cannot use the function RSA_get0_key in OpenSSL 1.0.0 to extract the values of n, e, d by reading the private key from a file and passing it as parameter to the aforementioned function.
It is not a programming issue, I mean, I know how to use the functions, but I don't know if there is an alternative to do this.
Indeed, the warning that stops me during the compiling operation is the following:
warning: implicit declaration of function ‘RSA_get0_key’; did you mean ‘RSA_check_key’? [-Wimplicit-function-declaration]
Do you know how to do that? I check the manual here (https://www.openssl.org/docs/man1.0.2/man3/) but it seems that there isn't a properly function to make this. Further, I need to be compliant to OpenSSL 1.0.0.
Code
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rsa.h>
#include <openssl/obj_mac.h>
#include <openssl/rand.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pem.h>
int main()
{
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
RSA *privkey = RSA_new();
FILE *privkeyfile = fopen("private.key", "rb");
PEM_read_RSAPrivateKey(privkeyfile, &privkey, NULL, NULL);
fclose(privkeyfile);
BIGNUM *n, *e, *d = NULL;
RSA_get0_key(privkey,&n,&e,&d);
return 0;
}
The RSA_get0_key function was added in OpenSSL 1.1.0 as an abstraction to retrieve the n, e, and d values for an RSA key. For earlier versions you need to access the fields directly.
n = privkey->n;
e = privkey->e;
d = privkey->d;
If you want your code to be able to handle 1.0.x and 1.1.x, you can check the value of OPENSSL_VERSION_NUMBER:
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
RSA_get0_key(privkey, &n, &e, &d);
#else
n = privkey->n;
e = privkey->e;
d = privkey->d;
#endif
I tried many guides and for some reason, my syscall isnt implementing correctly. ERRNO is 38 (function not implemented). Here are my steps to creating a basic one for me as a beginner:
I am doing this on a raspberry pi 64 bit. with linux 4.14.93
inside "/linux/" folder:
I created a folder called "mycall" with files mycall.c, mycall.h, & Makefile
mycall.c:
#include <linux/kernel.h>
#include <linux/init.h>
#include <sched.h>
#include <syscalls.h>
#include <linux/unistd.h>
#include "mycall.h"
asmlinkage long sys_mysyscall(int *id, int username, int *size)
{
printk("hello");
return 0;
}
mycall.h:
asmlinkage long sys_mysyscall(int *id, int username, int *size);
Makefile:
obj-y := mycall.o
then I went into all of the places where I THOUGHT i should declare the syscall.
// inside of /linux/include/linux/syscalls.h
asmlinkage long sys_mysyscall(int __user *myid, int username, int __user *size);
then to
// inside of /linux/arch/arm/tools/syscall.tbl
398 common mysyscall sys_mysyscall
finally i added
// inside of /linux/Makefile
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypt/ block/ mycall/
thats my set up. then the final thing I do is make a userspace in /linux directory
userspace.c:
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <pwd.h>
int main ()
{
int id = 0;
int username = 7;
int size = 2;
int ret_val = syscall(398, &id, username, &size);
printf("%d\n", ret_val);
return 0;
}
// recompiling & copying . then I run the userspace again
cd ~/linux
KERNEL=kernel7
make bcm2709_defconfig
make -j4 zImage modules dtbs
sudo make INSTALL_MOD_PATH=/root modules_install
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm/boot/zImage /boot/$KERNEL.img
the return value is -1 so this doesn't work.
please let me know what I'm doing wrong. I have read several implementation guides and have no idea how I'm implementing this wrong.
The following MEXed C code simply makes calls to magma to invert a matrix. The stand alone C code (which is also posted) works, but the mex code crashes.
I've triple checked the documentation, verified that other magma functions work as expected, and posted on the Magma forum and was told my code is fine (this post is a cross post from Magma forum). This means that the problem is with mex. I would like to know what is causing the mex code to seg-fault and how to get it to run as expected.
Mexed code:
#include <mex.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stddef.h>
#include <magma_v2.h>
#include <cuda_runtime.h>
void mat2magma(magmaDoubleComplex* p, double* pr, double* pi,int numElements)
{
int j=0;
for(j=0;j<numElements;j++){
p[j].x=pr[j];
p[j].y=pi[j];
}
}
void magma2mat(magmaDoubleComplex* p, double* pr, double* pi,int numElements)
{
int j=0;
for(j=0;j<numElements;j++){
pr[j]= p[j].x;
pi[j]= p[j].y;
}
}
/*gateway function*/
void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[]) {
/*initialize magma*/
magma_init();
magma_queue_t queue = NULL;
magma_device_t dev;
magma_getdevice(&dev);
magma_queue_create(dev,&queue );
magma_int_t m,ldwork,info;
magma_int_t *piv;
magmaDoubleComplex *a,*da,*dwork;
/* Matlab -> Host */
m=mxGetM(prhs[0]);
piv=(magma_int_t*) malloc(m*sizeof(magma_int_t));
magma_zmalloc_cpu(&a,m*m);
mat2magma(a,mxGetPr(prhs[0]),mxGetPi(prhs[0]),m*m);
ldwork = m*magma_get_zgetri_nb(m);
/* Host -> GPU */
magma_zmalloc(&dwork,ldwork);
magma_zmalloc(&da,m*m);
magma_zsetmatrix(m,m,a,m,da,m,queue);
/*LU and Inverse */
magma_zgetrf_gpu(m,m,da,m,piv,&info);
magma_zgetri_gpu(m,da,m,piv,dwork,ldwork,&info);
/*GPU -> Host */
magma_zgetmatrix(m,m,da,m,a,m,queue);
/*Host -> Matlab*/
plhs[0] = mxCreateDoubleMatrix(m,m,mxCOMPLEX);
magma2mat(a,mxGetPr(plhs[0]),mxGetPi(plhs[0]),m*m);
free(a);
free(piv);
magma_free(dwork);
magma_free(da);
magma_queue_destroy(queue);
magma_finalize();
}
I compliled it with mex CC=gcc LDFLAGS="-lmagma -lcudart -lcublas" magmaZinv.c then from matlab, I ran:
a=magic(3)+magic(3)*1i;
magmaZinv(a)
Standalone C code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stddef.h>
#include <magma_v2.h>
#include <cuda_runtime.h>
#include <sys/time.h>
#include <time.h>
/*gateway function*/
int main() {
/*initialize magma*/
magma_init();
magma_queue_t queue = NULL;
magma_device_t dev;
magma_getdevice(&dev);
magma_queue_create(dev,&queue );
int m,ldwork,info;
int *piv;
magmaDoubleComplex *a,*da,*dwork;
/* allocate and initialize a = magic(3)+magic(3)*1i; */
m=3;
piv=(int*) malloc(m*sizeof(int));
ldwork = m*magma_get_zgetri_nb(m);
magma_zmalloc_cpu(&a,m*m);
a[0].x=8;a[0].y=8;
a[1].x=3;a[1].y=3;
a[2].x=4;a[2].y=4;
a[3].x=1;a[3].y=1;
a[4].x=5;a[4].y=5;
a[5].x=9;a[5].y=9;
a[6].x=6;a[6].y=6;
a[7].x=7;a[7].y=7;
a[8].x=2;a[8].y=2;
/* Host -> GPU */
magma_zmalloc(&dwork,ldwork);
magma_zmalloc(&da,m*m);
magma_zsetmatrix(m,m,a,m,da,m,queue);
/*LU and Inverse */
magma_zgetrf_gpu(m,m,da,m,piv,&info);
magma_zgetri_gpu(m,da,m,piv,dwork,ldwork,&info);
/*GPU -> Host */
magma_zgetmatrix(m,m,da,m,a,m,queue);
/* display inv(a) */
for (int i=0;i<(m*m);i++){
printf("%f +%fi\n",a[i].x,a[i].y);
}
/* free memory */
free(a);
free(piv);
magma_free(dwork);
magma_free(da);
magma_queue_destroy(queue);
magma_finalize();
return 0;
}
I compiled with: gcc -lmagma -lcudart Ccode.c -o Ccode.o
My sys admin has figured out why the standalone C code works while the mexed C code does not. I'll post the reason here incase it is helpful to anyone facing the same issues when using Magma from within Matlab.
The version of Matlab I was using was 2014a. The supported compiler for this version is 4.7.x. I was using a higher version of gcc to compile the code. I've never had a problem with using different versions of GCC with matlab, despite the warning it gives, but for the above code it does matter.
Compile with the MKL_ilp64 flag when using Magma with Matlab to ensure that magma_int_t is int64.
With these two suggestions, Magma can be mexed into matlab with no problems.
I'm writing a program for vocabulary training, for myself. And the program itself should be available in different languages, atm in German and English.
What I want is to have a main file which manage all and two separate files for the functions in the right language.
I compile all the files with:
gcc vocTrainer.c german_menue.c english_menue.c -o v.exe
But I get an error of multiple definition even though I only include one of the language files depending on your input.
Multiple defintion of 'orderOfVoc'
First defined here: collect2.exe error: ld returned 1 exit status
My code:
vocTrainer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "german_menue.h"
#include "english_menue.h"
int main(void)
{
char selectLang[1]; //store 1 for English or 2 for German
system("cls"); //clear screen
memset(selectLang,0,1); //set all fields in the array to 0
while(selectLang[1] != 1 && selectLang[1] != 2)
{
//select your language
printf("Choose language - Sprache auswaehlen:\n(1) - Englisch/English\n(2) - Deutsch/German\n");
scanf("%d",&selectLang[1]);
system("cls");
}
//language query
if(selectLang[1] == 2)
{
#include "german_menue.c"
}
else
{
#include "english_menue.c"
}
printf("Test of select Order: %d",orderOfVoc());
return 0;
}
german_menue.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "german_menue.h"
int orderOfVoc()
{
char selectOrder[1]; /*store the choosen order of vocabulary.
1 for one after another 2 for a random sequence of words*/
printf("Wie sollen die Vokabeln abgefragt werden?\n(1) - Der Reihe nach\n(2) - Zufaellig\n");
scanf("%d",&selectOrder[1]);
return selectOrder[1];
}
english_menue.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "german_menue.h"
int orderOfVoc()
{
char selectOrder[1]; /*store the choosen order of vocabulary.
1 for one after another 2 for a random sequence of words*/
printf("How do you want to learn the vocabulary?\n(1) - Vocabulary in order\n(2) - Random order\n");
scanf("%d",&selectOrder[1]);
return selectOrder[1];
}
german_menue.h
#ifndef GERMAN_MENUE_H //include guards
#define GERMAN_MENUE_H
extern int orderOfVoc();
#endif //GERMAN_MENUE_H
english_menue.h
#ifndef ENGLISH_MENUE_H //include guards
#define ENGLISH_MENUE_H
extern int orderOfVoc();
#endif //ENGLISH_MENUE_H
Primary Issue: In your code,
if(selectLang[1] == 2)
{
#include "german_menue.c"
}
else
{
#include "english_menue.c"
}
is not doing what you're thinking. There are may issues, like
#include is compile time operation (during preprocessing state) and essentially cannot be controlled at runtime.
You don't include source files. You compile and link them together. Your compilation statement looks correct. Just leave out the above mentioned code snippet from your code.
Just to add a bit detail regarding the reason behind the error you received, is because, you have #includeed the source files (which is essentially adding the source code of that .c file in vocTrainer.c file itself) and again, at compile time, you're putting the .c files. Thus, after compilation, at linking state, compiler sees more than one occurrences of orderOfVoc() which is why compiler is complaining.
Solution:
You remove different definition of orderOfVoc() function. Make use of the user selected value. Pass the value to the orderOfVoc() as an argument, and execute accordingly.
Secondary Issue(s): Apart from above issue(s), in your code, with a definition like
char selectLang[1];
writing
scanf("%d",&selectLang[1]);
is wrong, because
selectLang[1] is out of bound access. Array index in C starts from 0.
%d is not the correct formart specifier for char.
FWIW, char selectLang[1]; is functionally equivalent with char selectLang;
A modified version (not tested) for aforesaid approach:
select_menue.h
#ifndef SELECT_MENUE_H //include guards
#define SELECT_MENUE_H
//according to {store 1 for English or 2 for German}
#define ENGLISH 1
#define GERMAN 2
extern int orderOfVoc(int);
#endif //SELECT_MENUE_H
select_menue.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "select_menue.h"
int orderOfVoc(int lang)
{
int selectOrder = 0;
switch (lang)
{
case ENGLISH:
printf("How do you want to learn the vocabulary?\n(1) - Vocabulary in order\n(2) - Random order\n");
scanf("%d",&selectOrder); //add possible error check
break;
case GERMAN:
printf("Wie sollen die Vokabeln abgefragt werden?\n(1) - Der Reihe nach\n(2) - Zufaellig\n");
scanf("%d",&selectOrder); //add possible error check
break;
}
return selectOrder;
}
vocTrainer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "select_menu.h"
int main(void)
{
int selectLang = 0; //array not required, initialize in single statement
//store 1 for English or 2 for German
while(selectLang != 1 && selectLang != 2)
{
//select your language
printf("Choose language - Sprache auswaehlen:\n(1) - Englisch/English\n(2) - Deutsch/German\n");
scanf("%d",&selectLang);
}
printf("Test of select Order: %d",orderOfVoc(selectLang));
return 0;
}
#include is a preprocessor directive that includes the contents of the file named at compile time.
The code that conditionally includes stuff is executed at run time...not compile time. So both files are being compiled in. ( You're also including each file twice, once in the main function and once above it, which is just confusing and probably wrong, but we'll ignore that for now. )
You can't really conditionally include stuff at run time. You can use other preprocessor directives (#ifdef, etc. ) to conditionally include one or the other file at compile time, but for your purposes you really need to have some sort of global flag that each function in the included files uses to determine if it should display english or german, etc.
Internationalization of strings is a whole topic in itself. There are lots of ways to handle it, and some libraries to make it easier depending on your platform.
Here's one way you could handle the same scenari:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "german_menue.h"
char *getLocalizedString(int stringId)
{
// Pseudo-Code, not real C++
// Also ignores memory issues and deallocating strings when done
char *localizedString = LoadGermanOrEnglishStringBasedOnGlobalVarForLanguage(stringId);
return localizedString ;
}
int orderOfVoc()
{
int stringId = 1;//should be constant for this message
char *localizedString = getLocalizedString(stringId);
printf("%s", localizedString);
scanf("%d",&selectOrder[1]);
return selectOrder[1];
}
I've read through the Linux kernel documents on i2c and written a code to try to replicate the command i2cset -y 0 0x60 0x05 0xff
The code that I've written is here:
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <string.h>
int main(){
int file;
file = open("/dev/i2c-0", O_RDWR);
if (file < 0) {
exit(1);
}
int addr = 0x60;
if(ioctl(file, I2C_SLAVE, addr) < 0){
exit(1);
}
__u8 reg = 0x05;
__u8 res;
__u8 data = 0xff;
int written = write(file, ®, 1);
printf("write returned %d\n", written);
written = write(file, &data, 1);
printf("write returned %d\n", written);
}
When I compile and run this code I get:
write returned -1
write returned -1
I've tried to follow exactly what the docs tell me, my understanding is that the address is set first with the call to ioctl, then I need to write() the register and then the data that I want sent to the register.
I've also tried to use use SMbus, but I can't get my code to compile using this, it complains at the linking stage that it can't find the functions.
Have I made any mistakes in this code? I'm a beginner to i2c and don't have a lot of experience with c either.
EDIT: errno give the following message: Operation not supported. I am logged in as root on this machine though, so I don't think it can be a permissions thing, although I may be wrong.
The way I got around this problem was to use SMBus, in particular the functions i2c_smbus_write_byte_data and i2c_smbus_read_byte_data. I was able to use these functions to successfully read and write to the device.
I did have a little trouble finding these functions, I kept trying to download libraries using apt-get to install the appropriate header files. In the end I simply downloaded the files smbus.c and smbus.h.
Then the code I needed was:
#include <stdio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "smbus.h"
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
int main(){
int file;
file = open("/dev/i2c-0", O_RDWR);
if (file < 0) {
exit(1);
}
int addr = 0x60;
if(ioctl(file, I2C_SLAVE, addr) < 0){
exit(1);
}
__u8 reg = 0x05; /* Device register to access */
__s32 res;
res = i2c_smbus_write_byte_data(file, reg, 0xff);
close(file);
}
Then if I compile the smbus.c file: gcc -c smbus.c and myfile: gcc -c myfile.c, then link them: gcc smbus.o myfile.o -o myexe I get a working executable that runs my I2C command. Ofcourse, I have smbus.c and smbus.h in the same directory as myfile.c.
In C, you can check the content of the errno variable to get more details into what went wrong. It is automatically declared when including errno.h and you can get a more descriptive text by calling strerror(errno).
Have you checked that you had write access to /dev/i2c-0 ?