blues27
Posts: 1
Joined: Tue Sep 05, 2017 3:44 am

How to use SPI in kernel module

Sat Sep 09, 2017 8:48 am

I'm this topic is not proper for this board, please let me know the proper place to ask.

Recently, I have tried to use MCP3008 in the kernel module to make a device driver for the linear sensor.
When I write a code to read data through MCP3008 with wiringPi, it's possible to obtain the sensor value correctly, so I guess the connection between Raspberry Pi3 and MCP3008 has no problem.

But when I load the kernel module of the following code, the function "readadc(1)" in "gpiomod_init" returns strage value, but the function in timer call_back function"blink_timer_func" returns correct value. And when I checked SPI_CS(CE0) (Pin 26) with oscilloscope, only the CS signal for the function in timer call_back function is displayed.

I wonder why the function to access SPI does not work in the "gpiomod_init" and it works fine in the timer call_back.

Does anyone tell me what I should add/modify the code to make the function to access SPI work in the "gpiomod_init"?

Thanks
Kensuke TAKITA.

Code: Select all

include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

static const char DEVICE[] = "/dev/spidev0.1";
static uint8_t MODE = SPI_MODE_0;
static uint8_t BITS = 8;
static uint32_t CLOCK = 2000000;
static uint16_t DELAY = 0;

#define DIO11   17
#define DIO12   27

static struct timer_list blink_timer;
struct file *mcp3008;

static int setupadc(void)
{

  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_WR_MODE,(long unsigned int)(&MODE)) == -1) {
    printk(KERN_INFO "Can't set MODE");
    return -1;
  }
  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_RD_MODE,(long unsigned int)(&MODE)) == -1) {
    printk(KERN_INFO "Can't set MODE");
    return -1;
  }
  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_WR_BITS_PER_WORD,(long unsigned int)(&BITS)) == -1) {
    printk(KERN_INFO "Can't set number of BITS");
    return -1;
  }
  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_RD_BITS_PER_WORD,(long unsigned int)(&BITS)) == -1) {
    printk(KERN_INFO "Can't set number of BITS");
    return -1;
  }
  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_WR_MAX_SPEED_HZ,(long unsigned int)(&CLOCK)) == -1) {
    printk(KERN_INFO "Can't set write CLOCK");
    return -1;
  }
  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_RD_MAX_SPEED_HZ,(long unsigned int)(&CLOCK)) == -1) {
    printk(KERN_INFO "Can't set read CLOCK");
    return -1;
  }
  return 0;
}

uint16_t readadc(uint8_t channel)
{
  uint8_t tx[3];
  uint8_t rx[3];
  uint16_t ret ;
  
  struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long)tx,
    .rx_buf = (unsigned long)rx,
    .len = ARRAY_SIZE(tx),
    .delay_usecs = DELAY,
    .speed_hz = CLOCK,
    .bits_per_word = BITS,
  };

  tx[0] = 0x01 ;
  tx[1] = 0x80 | (channel&7)<<4;
  tx[2] = 0;

  if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_MESSAGE(1),(long unsigned int)(&tr)) == -1) {
    printk(KERN_INFO "IO Error");
  }
  ret = (((uint16_t)rx[1] << 8) & 0x300) | ((uint16_t)rx[2] & 0xFF) ;

  return (ret);
}

static void blink_timer_func(unsigned long data)
{
  printk(KERN_INFO "spi read data1  %d\n", readadc(1));
  ndelay(1000);
  printk(KERN_INFO "spi read data2  %d\n", readadc(1));
  ndelay(1000);

  blink_timer.expires = jiffies + (1*HZ);
  add_timer(&blink_timer);
}

static int __init gpiomod_init(void)
{
  int ret = 0;

  mcp3008 = filp_open(DEVICE,O_RDWR,O_RDWR);

  printk(KERN_INFO "mcp3008 opened %d  \n",PTR_RET(mcp3008));

  if (IS_ERR(mcp3008)) {
    printk(KERN_INFO "error no is %ld\n", PTR_ERR(mcp3008));
  }
  if (!mcp3008) {
    printk(KERN_INFO "can't open device file \n");
  }

  if(setupadc() == -1){
    printk(KERN_INFO "prepare failed \n");
  }
  ndelay(1000);

  printk(KERN_INFO "spi read data  %d\n", readadc(1));
  ndelay(1000);
  printk(KERN_INFO "%s\n", __func__);

  ret = gpio_request_one(DIO11, GPIOF_OUT_INIT_LOW, "DIO11");
  if (ret) {
    printk(KERN_ERR "Unable to request DIO11: %d\n", ret);
    return ret;
  }

  ret = gpio_request_one(DIO12, GPIOF_OUT_INIT_LOW, "DIO12");
  if (ret) {
    printk(KERN_ERR "Unable to request DIO12: %d\n", ret);
    return ret;
  }

  init_timer(&blink_timer);
  blink_timer.function = blink_timer_func;
  blink_timer.expires = jiffies + (1*HZ);
  add_timer(&blink_timer);

  return ret;
}

static void __exit gpiomod_exit(void)
{
  printk(KERN_INFO "%s\n", __func__);

  del_timer_sync(&blink_timer);

  if(!IS_ERR_OR_NULL(mcp3008))
    filp_close(mcp3008, 0);

  gpio_set_value(DIO11, 0);
  gpio_set_value(DIO12, 0);

  gpio_free(DIO11);
  gpio_free(DIO12);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kensuke TAKITA");
MODULE_DESCRIPTION("Kernel module using SPI.");

module_init(gpiomod_init);
module_exit(gpiomod_exit);

cesarcazal
Posts: 1
Joined: Mon Jul 22, 2019 4:37 pm

Re: How to use SPI in kernel module

Mon Jul 22, 2019 5:09 pm

Was there any solution to this problem? I tried the code listed here (which had the timer_setup already deprecated and the callback function also, but made it work by replacing by its equivalent functions).
Once tried I found that neither the gpio_init function nor the timer callback were able to read anything from the SPI port (the CS pin remained in the same state always, after checking with the oscilloscope).
Is there any correct/better/working-at-least way to work with the SPI from the Kernel Space? :D

Return to “Interfacing (DSI, CSI, I2C, etc.)”