前两天写了一个<驱动模块使用I2C总线范例>。由于SPI和I2C类似,加上有空闲时间,故参考之前写的I2C实现了这个SPI模块。代码如下,这个代码未经调试,我目前的板子没有用到SPI接口,但是总体架构应该没错的。
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <linux/spi/spi.h>
struct spi_api {
struct list_head list;
struct spi_device *spi;
};
#define SPI_MINORS 32
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH /
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP /
| SPI_NO_CS | SPI_READY)
static LIST_HEAD(spi_api_list);
static DEFINE_SPINLOCK(spi_api_list_lock);
static struct spi_api *get_spi_api(int bus_id)
{
struct spi_api *spi_api;
spin_lock(&spi_api_list_lock);
list_for_each_entry(spi_api, &spi_api_list, list) {
if (spi_api->spi->master->bus_num == bus_id)
goto found;
}
spi_api = NULL;
found:
spin_unlock(&spi_api_list_lock);
return spi_api;
}
static struct spi_api *add_spi_api(struct spi_device *spi)
{
struct spi_api *spi_api;
if (spi->master->bus_num >= SPI_MINORS) {
printk(KERN_ERR "spi_api: Out of device minors (%d)/n",
spi->master->bus_num);
return NULL;
}
spi_api = kzalloc(sizeof(*spi_api), GFP_KERNEL);
if (!spi_api)
return NULL;
spi_api->spi = spi;
spin_lock(&spi_api_list_lock);
list_add_tail(&spi_api->list, &spi_api_list);
spin_unlock(&spi_api_list_lock);
return spi_api;
}
static void del_spi_api(struct spi_api *spi_api)
{
spin_lock(&spi_api_list_lock);
list_del(&spi_api->list);
spin_unlock(&spi_api_list_lock);
kfree(spi_api);
}
static int spi_api_do_set(int bus_id,
u8 mode,
u8 bits_per_word,
u8 max_speed_hz)
{
struct spi_device *spi;
struct spi_api *spi_api = get_spi_api(bus_id);
if (!spi_api)
return -ENODEV;
spi = spi_api->spi;
spi->mode &= ~SPI_MODE_MASK;
spi->mode |= mode;
spi->bits_per_word = bits_per_word;
spi->max_speed_hz = max_speed_hz;
return spi_setup(spi);
}
static int spi_api_do_write(int bus_id, const u8 *buf, size_t len)
{
struct spi_api *spi_api = get_spi_api(bus_id);
if (!spi_api)
return -ENODEV;
return spi_write(spi_api->spi, buf, len);
}
static int spi_api_do_read(int bus_id, u8 *buf, size_t len)
{
struct spi_api *spi_api = get_spi_api(bus_id);
if (!spi_api)
return -ENODEV;
return spi_read(spi_api->spi, buf, len);
}
static int spi_api_do_write_then_read(int bus_id,
const u8 *txbuf, unsigned n_tx,
u8 *rxbuf, unsigned n_rx)
{
struct spi_api *spi_api = get_spi_api(bus_id);
if (!spi_api)
return -ENODEV;
return spi_write_then_read(spi_api->spi, txbuf, n_tx, rxbuf, n_rx);
}
static int spi_api_probe(struct spi_device *spi)
{
add_spi_api(spi);
printk(KERN_INFO "spi_api_probe device[%d]/n", spi->master->bus_num);
return 0;
}
static int spi_api_remove(struct spi_device *spi)
{
struct spi_api * spi_api = get_spi_api(spi->master->bus_num);
if (spi_api)
del_spi_api(spi_api);
return 0;
}
static struct spi_driver spi_api_driver = {
.driver = {
.name = "SPI-API",
.owner = THIS_MODULE,
},
.probe = spi_api_probe,
.remove = spi_api_remove,
};
static int __init spi_api_init(void)
{
int ret = spi_register_driver(&spi_api_driver);
if (ret) {
printk(KERN_ERR "[%s] Driver registration failed, module not inserted./n", __func__);
return ret;
}
printk("spi_api_init/n");
return 0 ;
}
static void __exit spi_api_exit(void)
{
spi_unregister_driver(&spi_api_driver);
}
MODULE_AUTHOR("Loon, <sepnic@gmail.com>");
MODULE_DESCRIPTION("SPI spi_api Driver");
MODULE_LICENSE("GPL");
module_init(spi_api_init);
module_exit(spi_api_exit);
EXPORT_SYMBOL_GPL(spi_api_do_set);
EXPORT_SYMBOL_GPL(spi_api_do_write);
EXPORT_SYMBOL_GPL(spi_api_do_read);
EXPORT_SYMBOL_GPL(spi_api_do_write_then_read);