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);