Linux - Accessing SPI Bus from User Space - Part II

In this post we will see how to send and receive SPI messages to the device from userspace.

You can open the SPI device by using the below function call.


int fd = 0;
fd = open("/dev/spidev2.0", O_RDWR);
if (fd < 0)
{
printf("SPI : can't open device");
return -1;
}
You can now set the configuration for the SPI using ioctl calls.

To set the SPI mode
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
{
printf("SPI : can't set spi mode");
return 1;
}
To get the SPI mode
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
{
printf("SPI : can't get spi mode");
return 1;
}
Other SPI Bus properties can be manipulated in the same way.

Now the send a message over SPI Bus, You will need to fill up the message structure and pass it down.
struct spi_ioc_transfer mesg[1] = { 0, };

mesg[0].tx_buf = (unsigned long)out_buffer;
mesg[0].len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
To receive SPI message
struct spi_ioc_transfer mesg[1] = { 0, };

mesg[0].rx_buf = (unsigned long)out_buffer;
mesg[0].len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
There may be requirements where a write has to be followed by a read without toggling the Chip Select signal. You can form 2 messages as below to handle such situations.
int spidev_if_write_read(unsigned int num_out_bytes,
unsigned char *out_buffer,
unsigned int num_in_bytes,
unsigned char *in_buffer)
{
struct spi_ioc_transfer mesg[2] = { 0, };
uint8_t num_tr = 0;
int ret;

if((out_buffer != NULL) && (num_out_bytes != 0))
{
mesg[0].tx_buf = (unsigned long)out_buffer;
mesg[0].rx_buf = (unsigned long)NULL;
mesg[0].len = num_out_bytes;
mesg[0].cs_change = 0;
num_tr++;
}

if((in_buffer != NULL) && (num_in_bytes != 0))
{
mesg[1].tx_buf = (unsigned long)NULL;
mesg[1].rx_buf = (unsigned long)in_buffer;
mesg[1].len = num_in_bytes;
num_tr++;
}

if(num_tr > 0)
{
ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), mesg);
if(ret == 1)
{
return 1;
}
}

return 0;
}
The variable cs_change=0 disables the change in CS signal state between the two transactions.

Linux - Accessing SPI Bus from User Space - Part I

There needs to be a way to communicate with your hardware to write a complex device driver. In this post we will see how we can use the SPI Bus to communicate with the device from the user space.

You`ll need to configure your kernel to enable the SPI communication from userspace.

Let's first see the basic Kernel Configs needed to enable SPI support.
CONFIG_SPI
CONFIG_SPI_MASTER
CONFIG_SPI_SPIDEV

The CONFIG_SPI_SPIDEV enables the Userspace SPI framework for us.

Most Processors comes with a built in SPI Bus Module for greater performance and ease of use. If your platform happens to have one with a matching linux device driver. Enable the appropriate config.

E.g) For OMAP24xx or OMAP34xx Platforms you`ll need to enable CONFIG_SPI_OMAP24XX to use the inbuilt McSPI module of the processor.

You will now need to configure your board file to export the SPI bus to Userspace.

Add this piece of code in your board*.c file.

/* Structure for configuing McSPI */
static struct spi_board_info board_spi3_board_info[] = {
{
.modalias = "spidev", /* Expose spi to userspace */
.max_speed_hz = 500000, /* SPI Speed. */
.bus_num = 3, /* McSPI Bus Number */
.chip_select = 0, /* ChipSelect for McSPI */
.mode = 0, /* SPI Mode */
},
};

and add register the SPI bus in the board_init function in the board*.c file.
/*Register SPI device*/
spi_register_board_info(board_spi3_board_info, ARRAY_SIZE(board_spi3_board_info));

Now, when you boot up your system. You should see your spi device in the filesystem at /dev path.
e.g) /dev/spidev2.0 ( 2 - SPI bus number, 0 - Chip Select)

We can use the standard open,read,write and close api's along with /dev/spidevx.y device to send and receive SPI messages to the device.

In the next post we`ll see how to use the /dev/spidevx.y to send and receive messages.

Linux - Accessing GPIO from User Space

The Linux Kernel offers the GPIOLIB interface to access the GPIO pins from the User Space. To enable the GPIOLIB interface make sure you compile your kernel with the following options.

CONFIG_ARCH_REQUIRE_GPIOLIB
CONFIG_GPIOLIB
CONFIG_GPIO_SYSFS
Now, you can able to access and manipulate the GPIO's from the User Space using the standard C calls such as Open, Write, Read, Close etc.

Before manipulating the GPIO the specific GPIO pins has to be exported and configured appropriately. Each GPIO on the Processor will have a unique number, please refer to the processor manual.

To export an GPIO you need to open the export interface of the GPIOLIB and write the GPIO number you want to export.
int exportfd;
exportfd = open("/sys/class/gpio/export", O_WRONLY);
if (exportfd < 0)
{
printf("Cannot open GPIO to export it %d\n", errno);
return -1;
}

Write the GPIO you want to export and close the export interface.
write(exportfd, "149", 4);
close(exportfd)

Next, You will need to configure the direction of the GPIO using the direction interface of GPIOLIB.
int directionfd;
directionfd = open("/sys/class/gpio/gpio149/direction", O_RDWR);
if (directionfd < 0)
{
printf("Cannot open GPIO direction for 149\n");
return -1;
}

Write "in" if the GPIO is input or "out" if the GPIO is output and close the interface.
write(directionfd, "out", 4);
close(directionfd)

You can now use the value interface of GPIOLIB to set/clear the GPIO pin.
To make the GPIO line High
int valuefd;
valuefd = open("/sys/class/gpio/gpio149/value", O_RDWR);
if (valuefd < 0)
{
printf("Cannot open GPIO value for 149\n");
return -1;
}
write(valuefd, "1", 2);
close(valuefd);

To make the GPIO line Low
int valuefd;
valuefd = open("/sys/class/gpio/gpio149/value", O_RDWR);
if (valuefd < 0)
{
printf("Cannot open GPIO value for 149\n");
return -1;
}
write(valuefd, "0", 2);
close(valuefd);

You can use the read() function to read the state of the GPIO.

In the next post we will see how to pass the interrupts from Kernel Space to User Space.

Linux - User Space Device Drivers

The device drivers for Linux are best written in the Kernel Space to take advantage of the Kernel API's that are not available to the User space. However, there are reasons to write device drivers in user space. The advantage being you don't have to recompile and deploy the kernel for every change and possibility of having different licensing options etc.

The Linux Kernel offers some basic infrastructure to write a device driver in the User Space. The common basic requirement to write an device driver is the access to Interrupts, GPIO and some form of communication Bus (SPI / I2C).

In the subsequent posts we will see how to access each of hardware resource from the User Space.

Creating an Android Executable

Creating an Android executable is easy and straight forward with the Android source. Android is not GNU/Linux and it does not have access to standard glibc functions. Instead, android has its own C runtime called bionic. Your executable has to be linked with the bionic libraries instead of standard glibc.

The Android Build System searches every sub folder for Android.mk and executes it. The macros defined by the Android Build System makes sure that your executable are linked with the right libraries.

Steps for creating the android executable

1. Create a folder for your program in the Android Tree. If you not sure on where to create it put it under system folder.

2. Create a basic Android.mk file with the below contents.

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
my_program.c \
my_program_1.c

LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog

LOCAL_MODULE:= myprogram

include $(BUILD_EXECUTABLE)

3. Add you program's C files in the macro LOCAL_SRC_FILES.

4. Build and Flash the Android Source.

You should see your program "myprogram" under /system/bin folder.

You can launch you program using the Terminal Emulator application inside the Android Device OR connect the device with adb and execute from the adb shell.

Android on BeagleBoard

It has been a long long break and I am back with some experience on Android Device Drivers. I`ll follow up with more posts on writing code for Android Platform.

Hardware:
The BeagleBoard is a low cost OMAP3 developement platform from TI. Visit www.beagleboard.org. The BeagleBoard runs variety of software including Linux, Android, Symbian, Win CE etc. It comes with an expansion header for connecting your custom Hardware.

Software:
The Android distro that is used with BeagleBoard is called 0xdroid from 0xlabs. Visit here for download and build instructions.

You should be up and running in a day with your own build of 0xdroid on BeagleBoard.