Implement a custom SPI device driver

Hi all,
Currently I work on a implementation for a custom SPI protocol driver.
The goal is, that I have a single kernel module which can be used for a custom SPI device. The SPI devices (multiple devices of the same type) are connected on one or more SPI controller interfaces. (In my case it is a altera softIP)

In the dtb I declare my device with the name “vend,mySPI_slave_0” on the corresponding SPI controller. For one device, it works quite fine. But when I add a second device in the dtb, I have some troubles with the class declaration… So I received some error like: "sysfs: cannot create duplicate filename ‘/class/mySPIClass’ "

My question is, what is the state of the art, for creating a custom SPI device with access over sysfs…? Should I share the created class in a static variable, and if it initialized then use them again? or is it better to have a base module which create only the class and share them with other modules? But what is for the de-initialisation? Can I check if a class still in use, before I destroy the class? In future, I will implement some other modules which should use the same class…

Attached you will find some code snippet:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>

#define SPI_CLASS_NAME "mySPIClass"
#define MY_DEVICE_NAME_0 "mySPI_slave-0"
#define MY_DEVICE_NAME_1 "mySPI_slave-1"
#define MY_DEVICE_MAX_DEVICES (2)
#define  MY_DEVICE_USER_BUF_LENGTH (32)

static unsigned int my_device_addressRx = 0;
static char my_device_dataRx[MY_DEVICE_USER_BUF_LENGTH];
static unsigned char my_device_sizeRx;
static dev_t my_device_dev_t;
static struct device *my_device_dev;
static struct cdev *my_device_cdev;
static struct class *my_device_class;
static struct spi_device *my_device_spi;

struct spi_transfer spi_element[] = {
    {
        .len = 2,
        .cs_change = 0,
    }, {
        .len = 2,
    },
};


// (1) finding a match from full device-tree (with vendor part)
static const struct of_device_id my_device_of_match[] = {
    { 
      .compatible = "vend,mySPI_slave_0", 
      .data = (void *) 0,
    },
    { 
      .compatible = "vend,mySPI_slave_1", 
      .data = (void *) 0,
    },
    { }
};
MODULE_DEVICE_TABLE(of, my_device_of_match);

// (2) finding a match from stripped device-tree (no vendor part)
static const struct spi_device_id my_device_id[] = {
    { "mySPI_slave", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, my_device_id);

static int my_device_open(struct inode *inode, struct file *filp)
{    
    printk("mySPI_slave::my_device_open called.\n");
    return 0;
}

static int my_device_release(struct inode *inode, struct file *file)
{
    printk("mySPI_slave::my_device_release called.\n");
    return 0;
}

static ssize_t my_device_read(struct file *filp, char __user * buf, size_t lbuf, loff_t * ppos)
{
    printk("mySPI_slave::my_device_read called.\n");
    return lbuf;
}

static ssize_t my_device_write(struct file *filep, const char __user * buf, size_t lbuf, loff_t * ppos)
{
    printk("mySPI_slave::my_device_write called.\n");
    return lbuf;
}

static const struct file_operations my_device_fops = {
    .owner =    THIS_MODULE,
    .write =    my_device_write,
    .read =        my_device_read,
    .open =        my_device_open,
    .release =    my_device_release
};


// Initialize SPI interface...
static int my_device_probe(struct spi_device *spi)
{
    int err;
    const struct of_device_id *match;
    unsigned char ch16[] = {0x5A, 0x5A};    
    unsigned char rx16[] = {0x00, 0x00};
    int devData = 0;
    printk("mySPI_slave::my_device_probe called.\n");

    // check and read data from of_device_id...
    match = of_match_device(my_device_of_match, &spi->dev);
    if(!match) {
        printk("mySPI_slave::my_device_probe drvice not found in device tree...\n");
    } 
    else {
        devData = match->data;
        printk("mySPI_slave::my_device_probe data is: %d\n", devData);

    }

    spi->max_speed_hz = 1; // ?
    spi->bits_per_word = 16;
    spi->mode = (0);
    
    err = spi_setup(spi);
    if (err < 0) {
        printk("mySPI_slave::my_device_probe spi_setup failed!\n");
        return err;
    }

    printk("spi_setup ok, cs: %d\n", spi->chip_select);
    printk("start data transfer...\n");

    spi_element[0].tx_buf = ch16;
    spi_element[1].rx_buf = rx16;

    err = spi_sync_transfer(spi, spi_element, ARRAY_SIZE(spi_element));
    printk("data size: %d\n", sizeof(rx16));
    if (err < 0) {
        printk("mySPI_slave::my_device_probe spi_sync_transfer failed!\n");
        return err;
    }

    my_device_spi = spi;
    printk("transfer ok\n");
    

    // define a device class
    my_device_class = class_create(THIS_MODULE, SPI_CLASS_NAME);
    if (my_device_class == NULL) {
        printk("mySPI_slave::my_device_probe class_create failed!\n");
        return -1;
    }

    // create char device entry in sysfs...
    if (devData == 0) {
        err = alloc_chrdev_region(&my_device_dev_t, 0, MY_DEVICE_MAX_DEVICES, MY_DEVICE_NAME_0);
    } else {
        err = alloc_chrdev_region(&my_device_dev_t, 0, MY_DEVICE_MAX_DEVICES, MY_DEVICE_NAME_1);
    }
    if (err < 0) {
        printk("mySPI_slave::my_device_probe alloc_chrdev_region failed!\n");
        class_destroy(my_device_class);
        return err;
    }
    
    my_device_cdev = cdev_alloc();
    if (!(my_device_cdev)) {
        printk("mySPI_slave::my_device_probe cdev_alloc failed!\n");
        unregister_chrdev_region(my_device_dev_t, MY_DEVICE_MAX_DEVICES);
        class_destroy(my_device_class);
        return -1;
    }

    cdev_init(my_device_cdev, &my_device_fops);

    err = cdev_add(my_device_cdev, my_device_dev_t, MY_DEVICE_MAX_DEVICES);
    if(err < 0) {
        printk("mySPI_slave::my_device_probe cdev_add failed!\n");
        cdev_del(my_device_cdev);
        unregister_chrdev_region(my_device_dev_t, MY_DEVICE_MAX_DEVICES);
        class_destroy(my_device_class);
        return err;
    }

    if (devData == 0) {
        my_device_dev = device_create(my_device_class, NULL, my_device_dev_t, NULL, "%s", MY_DEVICE_NAME_0);
    } else {
        my_device_dev = device_create(my_device_class, NULL, my_device_dev_t, NULL, "%s", MY_DEVICE_NAME_1);
    }
    return 0;
}

static int my_device_remove(struct spi_device *spi)
{
    printk("my_device_remove() called.\n");
    device_destroy(my_device_class, my_device_dev_t);
    if(my_device_cdev) {
        cdev_del(my_device_cdev);
    }
    unregister_chrdev_region(my_device_dev_t, MY_DEVICE_MAX_DEVICES);
    class_destroy(my_device_class);
    return 0;
}

static struct spi_driver my_device_spi_driver = {
    .driver = {
        .owner =    THIS_MODULE,
        .name =        "mySPI_slave",
        .of_match_table = of_match_ptr(my_device_of_match),
    },
    .id_table =    my_device_id,
    .probe =    my_device_probe,
    .remove =    my_device_remove
};
module_spi_driver(my_device_spi_driver);

Hi Schnudi,
sorry I cannot help you but instead I’m trying to do the same thing with a board and I’m still not able to have the first spi device working. Could you please tell me what you exactly put in the dts file?
Are you using also a ‘real’ HW for the SPI device or you’re emulating it? I’m in the second case.
Thanks in advance!