Working on spidev.h and spidev.c translation

OK. I haven’t gotten any responses, so I’ll try again. This is the description of SPI ioctl transfer struct. It says “Holds pointer to userspace buffer with transmit data, or null. If no data is provided, zeroes are shifted out. : Holds pointer to userspace buffer for receive data, or null.”

Detailed Description
struct spi_ioc_transfer - describes a single SPI transfer : Holds pointer to userspace buffer with transmit data, or null. If no data is provided, zeroes are shifted out. : Holds pointer to userspace buffer for receive data, or null. : Length of tx and rx buffers, in bytes. : Temporary override of the device’s bitrate. : Temporary override of the device’s wordsize. : If nonzero, how long to delay after the last bit transfer before optionally deselecting the device before the next transfer. : True to deselect device before starting the next transfer.

In the C++ program, the author gets the addresses of the arrays passed into the function and puts them in the struct for transmit and receive:

int SPIDevice::transfer(unsigned char send[], unsigned char receive[], int length){
        struct spi_ioc_transfer transfer;


        transfer.tx_buf = (unsigned long) send;
        transfer.rx_buf = (unsigned long) receive;
        transfer.len = length;
        transfer.speed_hz = this->speed;
        transfer.bits_per_word = this->bits;
        transfer.delay_usecs = this->delay;
        transfer.pad = 0;
        int status = ioctl(this->file, SPI_IOC_MESSAGE(1), &transfer);
        if (status < 0) {
                perror("SPI: Transfer SPI_IOC_MESSAGE Failed");
                return -1;
        }
        return status;
}

I don’t know if I’m doing this correctly. I am using an NTuple instead of an Array.

send = (0b00000001, 0b10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
receive = (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)


function spi_transfer(spi::SPIDevice, send::NTuple{8,UInt8}, receive::NTuple{8, UInt8}, len::Int32)
    transfer = spi_ioc_transfer()
 
    #see <linux/spi/spidev.h> for details!
    transfer.tx_buf = pointer_from_objref(send)
    transfer.rx_buf = pointer_from_objref(receive)
    transfer.len = len #number of bytes in vector
    transfer.speed_hz = spi.speed
    transfer.delay_usecs = spi.delay
    transfer.bits_per_word = spi.bits
    transfer.pad = 0

    ret = ccall((:ioctl, "libc"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}...), spi.file, SPI_IOC_MESSAGE(1), transfer) 
    
    if (ret < 0) 
        error("SPI: Transfer SPI_IOC_MESSAGE Failed")
        return -1
    end

end

I’m still getting a -1 on the return. The other parameter values all match what are in the C++ code. I am just not sure of getting the address of the NTuple or whether I should just use an Array. AND YES I HAVE BEEN THROUGH Calling C and Fortran Code section a dozen times.

Using pointer_from_objref there doesn’t look correct – that’s a pointer to a (boxed) Julia object.

I’m not sure what the issue is, but at this point doing this kind of programming, you will probably save considerable time if you use a debugger. Doing that with ioctl is tricky because IIUC you can’t really step into it, and there’s no debug symbols. What you can do instead is create a dummy library containing a function with the same signature, set a breakpoint there, ccall it, and see if everything looks sane in the struct compared to the setup in C++. (for kicks you can also call ioctl directly inside the dummy function with the data passed from Julia).

Thanks for the suggestion. My next move was to go search for the actual ioctl code and see if I can see how it’s being used inside (as far as the struct).

The value is correct but it isn’t GC safe.

I found this conversion in Python. They seem to be converting the data arrays to strings. It uses ctypes.create_string_buffer () - this function creates a mutable character buffer. The returned object is a
ctypes array of c_char. It also uses ctypes.addressof() to get the address of the buffer. Finally, it uses struct.pack() to take all of the values and packs it. This function takes in a format of data and then the data itself. The first parameter defines how the data supplied in the second parameter should be laid out. The layout is: QQIIHBBBBH - Q - unsigned long long (8 bytes), I - unsigned int (4 bytes), H - unsigned short (2 bytes), B - unsigned char (1 byte).

Does this mean I should be laying this out as a string of characters (an array of characters)?

def transfer(self, data, speed=0, bits_per_word=0, delay=0):
                   
                """Perform full-duplex SPI transfer
        
                Args:
        
                    data: List of words to transmit
        
                    speed: Optional temporary bitrate override in Hz. 0 (default)
        
                        uses existing spidev speed setting.
        
                    bits_per_word: Optional temporary bits_per_word override. 0
        
                        (default) will use the current bits_per_word setting.
        
                    delay: Optional delay in usecs between sending the last bit and
      
                        deselecting the chip select line. 0 (default) for no delay.
        
                Returns:
        
                    List of words read from SPI bus during transfer
      
                """
        
                data = array.array('B', data).tostring()
        
                length = len(data)
        
                transmit_buffer = ctypes.create_string_buffer(data)
        
                receive_buffer = ctypes.create_string_buffer(length)
      
                spi_ioc_transfer = struct.pack(SPI._IOC_TRANSFER_FORMAT,
        
                                               ctypes.addressof(transmit_buffer),
        
                                               ctypes.addressof(receive_buffer),
        
                                               length, speed, delay, bits_per_word, 0,
        
                                               0, 0, 0)
        
                fcntl.ioctl(self.handle, SPI._IOC_MESSAGE, spi_ioc_transfer)
        
                return [ord(byte) for byte in ctypes.string_at(receive_buffer, length)]

All of the code is at: https://github.com/tomstokes/python-spi/blob/master/spi.py

Alright, well allowing that is just weird, but I guess there’s already an issue for it.

@yuyichao - I found this code at:

https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/spi/spidev.c

and I noticed the use of copy_from_user() for the tx and rx buffer. I looked up this method and saw:

These functions do a few things:
They check if the supplied userspace block is entirely within the user portion of the address space (access_ok()) - this prevents userspace applications from asking the kernel to read/write kernel addresses;

They return an error if any of the addresses are inaccessible, allowing the error to be returned to userspace (EFAULT) instead of crashing the kernel (this is implemented by special co-operation with the page fault handler, which specifically can detect when a fault occurs in one of the user memory access functions);

They allow architecture-specific magic, for example to ensure consistency on architectures with virtually-tagged caches, to disable protections like SMAP or to switch address spaces on architectures with separate user/kernel address spaces like S/390.

Do you think this may be why I’m getting a -1 returned from ioctl call (a kernel space vs user space issue)?

C++ code

static int spidev_message(struct spidev_data *spidev,
		struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
	struct spi_message	msg;
	struct spi_transfer	*k_xfers;
	struct spi_transfer	*k_tmp;
	struct spi_ioc_transfer *u_tmp;
	unsigned		n, total, tx_total, rx_total;
	u8			*tx_buf, *rx_buf;
	int			status = -EFAULT;

	spi_message_init(&msg);
	k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
	if (k_xfers == NULL)
		return -ENOMEM;

	/* Construct spi_message, copying any tx data to bounce buffer.
	 * We walk the array of user-provided transfers, using each one
	 * to initialize a kernel version of the same transfer.
	 */
	tx_buf = spidev->tx_buffer;
	rx_buf = spidev->rx_buffer;
	total = 0;
	tx_total = 0;
	rx_total = 0;
	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
			n;
			n--, k_tmp++, u_tmp++) {
		k_tmp->len = u_tmp->len;

		total += k_tmp->len;
		/* Since the function returns the total length of transfers
		 * on success, restrict the total to positive int values to
		 * avoid the return value looking like an error.  Also check
		 * each transfer length to avoid arithmetic overflow.
		 */
		if (total > INT_MAX || k_tmp->len > INT_MAX) {
			status = -EMSGSIZE;
			goto done;
		}

		if (u_tmp->rx_buf) {
			/* this transfer needs space in RX bounce buffer */
			rx_total += k_tmp->len;
			if (rx_total > bufsiz) {
				status = -EMSGSIZE;
				goto done;
			}
			k_tmp->rx_buf = rx_buf;
			if (!access_ok(VERIFY_WRITE, (u8 __user *)
						(uintptr_t) u_tmp->rx_buf,
						u_tmp->len))
				goto done;
			rx_buf += k_tmp->len;
		}
		if (u_tmp->tx_buf) {
			/* this transfer needs space in TX bounce buffer */
			tx_total += k_tmp->len;
			if (tx_total > bufsiz) {
				status = -EMSGSIZE;
				goto done;
			}
			k_tmp->tx_buf = tx_buf;
			if (copy_from_user(tx_buf, (const u8 __user *)
						(uintptr_t) u_tmp->tx_buf,
					u_tmp->len))
				goto done;
			tx_buf += k_tmp->len;
		}

		k_tmp->cs_change = !!u_tmp->cs_change;
		k_tmp->tx_nbits = u_tmp->tx_nbits;
		k_tmp->rx_nbits = u_tmp->rx_nbits;
		k_tmp->bits_per_word = u_tmp->bits_per_word;
		k_tmp->delay_usecs = u_tmp->delay_usecs;
		k_tmp->speed_hz = u_tmp->speed_hz;
		if (!k_tmp->speed_hz)
			k_tmp->speed_hz = spidev->speed_hz;
#ifdef VERBOSE
		dev_dbg(&spidev->spi->dev,
			"  xfer len %u %s%s%s%dbits %u usec %uHz\n",
			u_tmp->len,
			u_tmp->rx_buf ? "rx " : "",
			u_tmp->tx_buf ? "tx " : "",
			u_tmp->cs_change ? "cs " : "",
			u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
			u_tmp->delay_usecs,
			u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
		spi_message_add_tail(k_tmp, &msg);
	}

	status = spidev_sync(spidev, &msg);
	if (status < 0)
		goto done;

	/* copy any rx data out of bounce buffer */
	rx_buf = spidev->rx_buffer;
	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
		if (u_tmp->rx_buf) {
			if (__copy_to_user((u8 __user *)
					(uintptr_t) u_tmp->rx_buf, rx_buf,
					u_tmp->len)) {
				status = -EFAULT;
				goto done;
			}
			rx_buf += u_tmp->len;
		}
	}
	status = total;

done:
	kfree(k_xfers);
	return status;
}

No.

@Keno - I just saw your blog

https://juliacomputing.com/blog/2017/02/21/finding-ioctls-with-cxx.html

and, I was wondering if you had any ideas on the above?

Again, seriously: a week reading source code will save you 10 minutes in the debugger…

OK. I’ve written a dummy shared library to see what is getting passed in (like the ioctl function). I’m seeing the values of the array correctly that I am passing in via the struct. I, also, display the pointer address - which the shared library is using fine. So, I don’t understand why the actual ioctl function is not working correctly.

Here’s the Julia run:

julia> include("spidev_h.jl")

julia> include("spidev.jl")
spidev

julia> import spidev

julia> using spidev

julia> send = [0b00000001, 0b10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x01
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> receive = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> transfer = spi_ioc_transfer()
spi_ioc_transfer(0x000000046816f494, 0x0000000000015007, 0x00000004, 0x00000004, 0xf490, 0x16, 0x68, 0x0a, 0x00, 0x0000)

julia> transfer.tx_buf = pointer(send)
Ptr{UInt8} @0x6f7773b0


julia> transfer.rx_buf = pointer(receive)
Ptr{UInt8} @0x67fef0b0

julia> ret = ccall((:simioctl, "/home/julia-user/julia-0.6.0/bin/libsimioctl.so"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}...), 18, SPI_IOC_MESSAGE(1), transfer)

address of tx_buf: 6F7773B0
sizeof(tx_buf): 8
1
128
0
0
0
0
0
0
1


Here’s the C code:

#include <iostream>
#include <cstdio>
#include <linux/spi/spidev.h>

extern "C" {
int simioctl(int fd, unsigned long cmd, unsigned long arg)
{
   struct spi_ioc_transfer *ioc;        

   ioc = (struct spi_ioc_transfer *) arg;

   unsigned char *tx = (unsigned char *)&ioc->tx_buf;

   printf("address of tx_buf: %llX\n", ioc->tx_buf);

   int size_tx_buf = sizeof(ioc->tx_buf);
   printf("sizeof(tx_buf): %d\n", size_tx_buf);

   for (int i = 0; i < size_tx_buf; i++)
      printf("%d\n", tx[i]);

   return 1;
}
}


I just wanted to add that if I do this in the C code:

#include <iostream>
#include <cstdio>
#include <linux/spi/spidev.h>

extern "C" {
int simioctl(int fd, unsigned long cmd, unsigned long arg)
{
   struct spi_ioc_transfer *ioc;        

   ioc = (struct spi_ioc_transfer *) arg;

   unsigned char *tx = (unsigned char *) ioc->tx_buf;

   printf("address of tx_buf: %llX\n", ioc->tx_buf);

   int size_tx_buf = sizeof(ioc->tx_buf);

   printf("sizeof(tx_buf): %d\n", size_tx_buf);

   for (int i = 0; i < size_tx_buf; i++)
      printf("%d\n", tx[i]);

   tx[0] = 111;

   return 1;
}
}

the send array gets changed in Julia.

julia> include("spidev_h.jl")

julia> include("spidev.jl")
spidev

julia> import spidev

julia> using spidev

julia> send = [0b00000001, 0b10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x01
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> receive = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> len = 3
3

julia> transfer = spi_ioc_transfer()
spi_ioc_transfer(0x0000000367b3b104, 0x0000000000015007, 0x00000003, 0x00000020, 0xb100, 0xb3, 0x67, 0x00, 0x00, 0x0000)

julia> transfer.tx_buf = pointer(send)
Ptr{UInt8} @0x6f78d4f0

julia> transfer.rx_buf = pointer(receive)
Ptr{UInt8} @0x680a8b30

julia> ret = ccall((:simioctl, "/home/julia-user/julia-0.6.0/bin/libsimioctl.so"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}...), 18, SPI_IOC_MESSAGE(1), transfer)
address of tx_buf: 6F78D4F0
sizeof(tx_buf): 8
1
128
0
0
0
0
0
0
1

julia> send
8-element Array{UInt8,1}:
 0x6f
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

Ok, one more thing: call ioctl in the shared library with those arguments passed from Julia.

What OS and platform is this running on?

That was on my list to try next, tomorrow. I’m running 32-bit Julia 0.6.0 on a Raspberry Pi 3 -running the latest version of Raspbian.

If that works, then my next suggestion will be to put together a single file, minimum, complete example that someone else can test. It’s possible that we have an issue with varargs ABI on that platform.

OK. I’m not sure I’m doing this next part right. Since I’m simulating the ioctl call, do I just pass the arguments as is from my simulated call to the real call?

Here’s the C code:

#include <iostream>
#include <cstdio>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

extern "C" {
int simioctl(int fd, unsigned long cmd, unsigned long arg)
{
   struct spi_ioc_transfer *ioc;

   ioc = (struct spi_ioc_transfer *) arg;

   unsigned char *tx = (unsigned char *) ioc->tx_buf;

   printf("fd: %d\n", fd);
   printf("cmd: %lX\n", cmd);
   printf("spi_ioc_transfer arg\n\n");
   printf("address of tx_buf: %llX\n", ioc->tx_buf);
   printf("address of rx_buf: %llX\n", ioc->rx_buf);
   printf("ioc->len: %d\n", ioc->len);
   printf("ioc->speed_hz: %d\n", ioc->speed_hz);
   printf("ioc->delay_usecs: %d\n", ioc->delay_usecs);
   printf("ioc->bits_per_word: %d\n", ioc->bits_per_word);
   printf("ioc->pad: %d\n", ioc->pad);

   int size_tx_buf = sizeof(ioc->tx_buf);

   printf("sizeof(tx_buf): %d\n", size_tx_buf);

   for (int i = 0; i < size_tx_buf; i++)
      printf("%d\n", tx[i]);

   int status = ioctl(fd, cmd, &ioc);
   if (status < 0) {
      perror("SPI: Transfer SPI_IOC_MESSAGE Failed");
      return -1;
   }
   //tx[0] = 111;

   return 1;
}
}

Here’s the Julia code with output:

julia> include("spidev_h.jl")

julia> include("spidev.jl")
spidev

julia> import spidev

julia> using spidev

julia> function combineValues(upper::UInt8, lower::UInt8)
           return (Int16(upper) << 8) | Int16(lower)
       end
combineValues (generic function with 1 method)

julia> println("Starting RPi SPI ADC Example")
Starting RPi SPI ADC Example

julia> spi = spidev.SPIDevice(0,0)
spidev.SPIDevice(0x00, 0x00, 0, "/dev/spidev0.0", 0x03, 0x08, 0x00077240, 0x0000)

julia> ret = spidev.spi_open(spi)
0

julia> ret = spidev.spi_setMode(spi, UInt8(SPI_MODE_0))
0

julia> ret = spidev.spi_setBitsPerWord(spi, UInt8(8))
0

julia> ret = spidev.spi_setSpeed(spi, UInt32(1000000))      # Have access to SPI Device object
0

julia> println("The SPI ADC is setup")
The SPI ADC is setup

julia> send = [0b00000001, 0b10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x01
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> receive = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> len = 3
3

julia> transfer = spi_ioc_transfer()
spi_ioc_transfer(0x0000000167bfca84, 0x0000000000015007, 0x00000001, 0x00000001, 0xca80, 0xbf, 0x67, 0x00, 0x00, 0x0000)

julia> #see <linux/spi/spidev.h> for details!
           transfer.tx_buf = pointer(send)
Ptr{UInt8} @0x6823f290

julia> transfer.rx_buf = pointer(receive)
Ptr{UInt8} @0x681aeb10

julia> transfer.len = len #number of bytes in vector
3

julia> transfer.speed_hz = spi.speed
0x000f4240

julia> transfer.delay_usecs = spi.delay
0x0000

julia> transfer.bits_per_word = spi.bits
0x08

julia> transfer.pad = 0
0

julia> ret = ccall((:simioctl, "/home/julia-user/julia-0.6.0/bin/libsimioctl.so"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}...), spi.file, SPI_IOC_MESSAGE(1), transfer)
fd: 20
cmd: 40206B00
spi_ioc_transfer arg

address of tx_buf: 6823F290
address of rx_buf: 681AEB10
ioc->len: 3
ioc->speed_hz: 1000000
ioc->delay_usecs: 0
ioc->bits_per_word: 8
ioc->pad: 0
sizeof(tx_buf): 8
1
128
0
0
0
0
0
0
SPI: Transfer SPI_IOC_MESSAGE Failed: Bad file descriptor
-1

So, everything looks like it is getting passed in correctly. I check the SPI_IOC_MESSAGE(1) against the C version of the program, it’s the same as the Julia version.

julia-user@NODE-RPI3:~/julia-0.6.0/bin $ ./ADCExample
Starting RPi SPI ADC Example
SPI_IOC_WR_MAX_SPEED_HZ: 1074031364
SPI_IOC_RD_MAX_SPEED_HZ: 1074031364
SPI_IOC_WR_MODE: 1073834753
SPI_IOC_RD_MODE: 2147576577
SPI_IOC_WR_BITS_PER_WORD: 1073834755
SPI_IOC_RD_BITS_PER_WORD: 2147576579
sizeof spi_ioc_transfer: 32
_IOC_SIZEBITS: 14
SPI_MSGSIZE(N):64
_IOC(_IOC_WRITE,(107),(0),(_IOC_TYPECHECK(32))): 1075866368
_IOC(_IOC_WRITE,(107),(0),(_IOC_TYPECHECK(64))): 1077963520
_IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(2)]): 1077963520
SPI_IOC_MAGIC: k
sizeof char[SPI_MSGSIZE(1)]: 32
SPI_IOC_MESSAGE(1): 1075866368
SPI_IOC_MESSAGE(2): 1077963520
The SPI ADC is setup
Response bytes are 1,7
This is the value 263 out of 1024.
End of ERPi SPI ADC Example

1075866368 = 0x40206B00

OK. it may have something to do with Synchronous vs Asynchronous files. I do:

function spi_open(spi::SPIDevice)
    #cout << "Opening the file: " << filename.c_str() << endl;
    file = open(spi.filename,"r+")
	spi.file = Base.Libc.fd(file)
	if (spi.file < 0)
		error("SPI: Can't open device.")
		return -1
    end

	return 0
end

This seems to work with most of the SPI functions that I’m calling (spidev.spi_setMode(), spidev.spi_setBitsPerWord(), spidev.spi_setSpeed()). But, I noticed the Bad File Descriptor Error I got. So, I might be on to something here.

I’m at a loss, now. I added a function to the shared library to make sure the file descriptor that is being passed in was still valid.

#include <iostream>
#include <cstdio>
#include <fcntl.h>
#include <errno.h> 
/* Not technically required, but needed on some UNIX distributions */
#include <sys/types.h>
#include <sys/stat.h>

#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

int fd_is_valid(int fd)
{
    return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}

extern "C" {
int simioctl(int fd, unsigned long cmd, unsigned long arg)
{
   struct spi_ioc_transfer *ioc;	

   ioc = (struct spi_ioc_transfer *) arg;

   int fdChk = fd_is_valid(fd);
   printf("fd_is_valid(fd): %d\n", fdChk);

   //if ((file = ::open("/dev/spidev0.0", O_RDWR))<0){
   //             perror("SPI: Can't open device.");
   //             return -1;
   //     }


   unsigned char *tx = (unsigned char *) ioc->tx_buf;
   
   printf("fd: %d\n", fd);
   //printf("file: %d\n", file);
   printf("cmd: %lX\n", cmd);
   printf("spi_ioc_transfer arg\n\n");
   printf("address of tx_buf: %llX\n", ioc->tx_buf);
   printf("address of rx_buf: %llX\n", ioc->rx_buf);
   printf("ioc->len: %d\n", ioc->len);
   printf("ioc->speed_hz: %d\n", ioc->speed_hz);
   printf("ioc->delay_usecs: %d\n", ioc->delay_usecs);
   printf("ioc->bits_per_word: %d\n", ioc->bits_per_word);
   printf("ioc->pad: %d\n", ioc->pad);

   int size_tx_buf = sizeof(ioc->tx_buf);

   printf("sizeof(tx_buf): %d\n", size_tx_buf);

   for (int i = 0; i < size_tx_buf; i++)
      printf("%d\n", tx[i]);

   int status = ioctl(fd, cmd, arg);
   if (status < 0) {
      perror("SPI: Transfer SPI_IOC_MESSAGE Failed");
      return status;
   }
   //tx[0] = 111;

   return 1;
}

Running the code in Julia, I can see the file descriptor is still valid when the shared library is called.

julia> include("spidev_h.jl")

julia> include("spidev.jl")
spidev

julia> import spidev

julia> using spidev

julia> function combineValues(upper::UInt8, lower::UInt8)
           return (Int16(upper) << 8) | Int16(lower)
       end
combineValues (generic function with 1 method)

julia> println("Starting RPi SPI ADC Example")
Starting RPi SPI ADC Example

julia> spi = spidev.SPIDevice(0,0)
spidev.SPIDevice(0x00, 0x00, 0, "/dev/spidev0.0", 0x03, 0x08, 0x00077240, 0x0000)

julia> ret = spidev.spi_open(spi)
0

julia> ret = spidev.spi_setMode(spi, UInt8(SPI_MODE_0))
0

julia> ret = spidev.spi_setBitsPerWord(spi, UInt8(8))
0

julia> ret = spidev.spi_setSpeed(spi, UInt32(1000000))      # Have access to SPI Device object
0

julia> println("The SPI ADC is setup")
The SPI ADC is setup

julia> send = [0b00000001, 0b10000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x01
 0x80
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> spi
spidev.SPIDevice(0x00, 0x00, 20, "/dev/spidev0.0", 0x00, 0x08, 0x000f4240, 0x0000)

julia> receive = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
8-element Array{UInt8,1}:
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00
 0x00

julia> len = 3
3

julia> transfer = spi_ioc_transfer()
spi_ioc_transfer(0x67cc77b067cc77a0, 0x67cc786067cc77c0, 0x67cc7870, 0x67cc7880, 0x7890, 0xcc, 0x67, 0xa0, 0x78, 0x67cc)

julia> #see <linux/spi/spidev.h> for details!
           transfer.tx_buf = pointer(send)
Ptr{UInt8} @0x68325910

julia> transfer.rx_buf = pointer(receive)
Ptr{UInt8} @0x68348770

julia> transfer.len = len #number of bytes in vector
3

julia> transfer.speed_hz = spi.speed
0x000f4240

julia> transfer.delay_usecs = spi.delay
0x0000

julia> transfer.bits_per_word = spi.bits
0x08

julia> transfer.pad = 0
0

julia> transfer
spi_ioc_transfer(0x0000000068325910, 0x0000000068348770, 0x00000003, 0x000f4240, 0x0000, 0x08, 0x67, 0xa0, 0x78, 0x0000)

julia> spi.file
20

julia> ret = ccall((:simioctl, "/home/julia-user/julia-0.6.0/bin/libsimioctl.so"), Cint, (Cint, Clong, Ref{spi_ioc_transfer}), spi.file, SPI_IOC_MESSAGE(1), transfer)
fd_is_valid(fd): 0
fd: 20
cmd: 40206B00
spi_ioc_transfer arg

address of tx_buf: 68325910
address of rx_buf: 68348770
ioc->len: 3
ioc->speed_hz: 1000000
ioc->delay_usecs: 0
ioc->bits_per_word: 8
ioc->pad: 0
sizeof(tx_buf): 8
1
128
0
0
0
0
0
0
SPI: Transfer SPI_IOC_MESSAGE Failed: Bad file descriptor
-1

So, the file descriptor seems to be working with the other SPI functions (mentioned in previous post), but something is happening in the shared library (and it makes me wonder if it’s the same issue with running it straight in Julia).

If anyone wants to take a look at all of the code I am working on, go to:

https://github.com/fapplin/Julia_On_Development_Boards

They are in the spidev directory.

The Julia files are:

spidev_h.jl (based on spidev.h)
spidev.jl (loosely based on Derek Molloy's routines found in BusDevice.h, BusDevice.cpp, SPIDevice.h, and SPIDevice.cpp)
spiADC_MCP3008.jl (based on Derek Molloy's ADCExample.cpp)

The C++ files are located in the same directory.

The code is being run on 32-bit Julia 0.6.0 that is running on a Raspberry Pi 3.
The Pi is running raspbian:
PRETTY_NAME=“Raspbian GNU/Linux 8 (jessie)”
NAME=“Raspbian GNU/Linux”
VERSION_ID=“8”
VERSION=“8 (jessie)”
ID=raspbian
Linux NODE-RPI3 4.9.35-v7+ #1014 SMP Fri Jun 30 14:47:43 BST 2017 armv7l GNU/Linux

If you want to wire up the hardware for the example that I’ve been working on, you can look at:

http://www.hertaville.com/interfacing-an-spi-adc-mcp3008-chip-to-the-raspberry-pi-using-c.html

Try replacing open(...) with direct ccall(:open, ...) in your spi_open function. You are only taking the fd integer – you’re not holding a reference to the Julia object returned by file = open(...) anywhere. file could be garbage collected, and maybe there is a finalizer which closes the handle.

(that said, I don’t think a finalizer should ever be run during a ccall, so if the fd really is valid at the beginning of simioctl then … :grey_question: … but try the change above anyway!)