#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/spi/spi.h>
#include <asm/uaccess.h>
#define AD7490_IOCTL_READ_AD _IOR(0, 1, int)
static struct {
struct spi_device *spi;
struct class *ad7490_class;
int major;
struct mutex lock;
} ad7490_data;
/*
* This function initializes the SPI device parameters.
*/
static inline int spi_nor_setup(struct spi_device *spi, u8 bst_len)
{
spi->bits_per_word = bst_len << 3;
return spi_setup(spi);
}
#define SPI_FIFOSIZE 24 /* Bust size in bytes */
/*
* This function perform spi read/write transfer.
*/
static int spi_read_write(struct spi_device *spi, u8 * buf, u32 len)
{
struct spi_message m;
struct spi_transfer t;
if (len > SPI_FIFOSIZE || len <= 0)
return -1;
spi_nor_setup(spi, len);
spi_message_init(&m);
memset(&t, 0, sizeof t);
t.tx_buf = buf;
t.rx_buf = buf;
t.len = (len + 3) / 4;
spi_message_add_tail(&t, &m);
if (spi_sync(spi, &m) != 0) {
printk(KERN_ERR "%s: error\n", __func__);
return -1;
}
dev_dbg(&spi->dev, "%s: len: 0x%x success\n", __func__, len);
return 0;
}
static ssize_t ad7490_read(struct file *file, char __user *buf, size_t len,
loff_t *ppos)
{
return 0;
}
static ssize_t ad7490_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
return 0;
}
static int ad7490_open(struct inode *inode, struct file *file)
{
return 0;
}
static int ad7490_release(struct inode *inode, struct file *file)
{
return 0;
}
/*
* Input: arg is a pointer to channel
* Output: arg is a pointer to AD value
*/
static int ad7490_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
u8 command[2];
int ret = 0, i, channel, value;
int __user *argp = (int __user *)arg;
pr_debug("ioctl cmd %d is issued...\n", cmd);
switch (cmd) {
case AD7490_IOCTL_READ_AD:
if (copy_from_user(&channel, argp, sizeof(int)))
return -EFAULT;
if ((channel < 0) || (channel > 15))
return -EINVAL;
mutex_lock(&ad7490_data.lock);//获取信号量锁存
for (i = 0; i < 2; i++) {
command[1] = 0x83 | (channel << 2);//add channel request to 'conctrl'
command[0] = 0x70;
ret = spi_read_write(ad7490_data.spi, command, 2);
if (ret)
break;
}
mutex_unlock(&ad7490_data.lock);//释放锁存信号量
if (ret)
return -EIO;
if (channel != (command[1] >> 4)) {
dev_err(&ad7490_data.spi->dev, "AD7490: channel not match, %d -> %d\n", channel, command[1] >> 4);
return -EINVAL;
}
value = ((command[1] & 0xf) << 8) | command[0];
if (copy_to_user(argp, &value, sizeof(value)))
return -EFAULT;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
//设备文件访问操作接口
static const struct file_operations ad7490_fops = {
.owner = THIS_MODULE,
.read = ad7490_read,
.write = ad7490_write,
.open = ad7490_open,
.release = ad7490_release,
.ioctl = ad7490_ioctl,
};
static int ad7490_probe(struct spi_device *spi) //监测系统是否有AD7490设备,并进行设备注册
{
int ret = 0;
struct device *dev;
mutex_init(&ad7490_data.lock); //初始化lock信号量
ad7490_data.spi = spi_dev_get(spi); //获取SPI设备
ad7490_data.major = 0; //定义主设备号
ret = register_chrdev(ad7490_data.major, "ad7490", &ad7490_fops);//注册字符设备
if (ret < 0)
return ret;
if (ad7490_data.major == 0) {
ad7490_data.major = ret;
printk(KERN_INFO "AD7490: major number %d\n", ad7490_data.major);
}
/* create class and device for udev information */
ad7490_data.ad7490_class = class_create(THIS_MODULE, "ad7490");//创建ad7490设备类
if (IS_ERR(ad7490_data.ad7490_class)) {
dev_err(&ad7490_data.spi->dev, "AD7490: failed to create ad7490 class\n");
goto char_dev_remove;
}
dev = device_create(ad7490_data.ad7490_class, NULL,
MKDEV(ad7490_data.major, 0), NULL, "ad7490");//创建设备节点
if (IS_ERR(dev)) {
dev_err(&ad7490_data.spi->dev,
"AD7490: failed to create class device\n");
goto class_remove;
}
return 0;
class_remove:
class_destroy(ad7490_data.ad7490_class);
char_dev_remove:
unregister_chrdev(ad7490_data.major, "ad7490");//注销字符设备
return -ENODEV;
}
static int __devexit ad7490_remove(struct spi_device *spi)
{
device_destroy(ad7490_data.ad7490_class, MKDEV(ad7490_data.major, 0));
class_destroy(ad7490_data.ad7490_class);
unregister_chrdev(ad7490_data.major, "ad7490");
return 0;
}
static struct spi_driver ad7490_driver = {
.driver = {
.name = "ad7490",
.owner = THIS_MODULE,
},
.probe = ad7490_probe,
.remove = __devexit_p(ad7490_remove),
};
static int __init ad7490_init(void)
{
return spi_register_driver(&ad7490_driver);
}
module_init(ad7490_init);
static void __exit ad7490_exit(void)
{
spi_unregister_driver(&ad7490_driver);
}
module_exit(ad7490_exit);
MODULE_DESCRIPTION("Driver for AD7490");
MODULE_AUTHOR("Electronics, Inc");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:ad7490");