SPI总线框架+通用字符设备驱动模板

内核版本:4.14.0,基于设备树

#include <linux/module.h> 
#include <linux/cdev.h> 
#include <linux/spi/spi.h> 
#include <linux/uaccess.h> 

#define DEVICE_CNT		1
#define DEVICE_NAME		"spidev"			/* Device name */
#define COMPAT_PROPT	"navigator,spidev"	/* Compatible property of the device matched with this driver. */

/* Device information structure. */
struct dev_info { 
	struct spi_device *spidev;
	dev_t devid;						/* Device ID */ 
	int major;							/* Major device id */ 
	int minor;							/* Minor device id */ 
	struct cdev cdev;
	struct class *class;
	struct device *device;
};

struct dev_info device_info;

/* 
 * @description :	Open the device.
 * @param – inode :	The inode passed to the driver.
 * @param - filp :	Device file, the file structure has a member variable called 'private_data', 
 * 					which is normally pointed to the device structure when it is opened.
 * @return :		0: Successful; Others: Failed.
 */
static int device_open(struct inode *inode, struct file *filp) 
{ 
	filp->private_data = &device_info;		/* Set private data.*/

	return 0; 
} 

/* 
 * @description :	Read from the device.
 * @param - filp :	The device file (file descriptor) to open.
 * @param - buf :	The data buffer returns to the user space.
 * @param - cnt :	The length of the data to be read.
 * @param - offt :	Offset relative to the first address of the file.
 * @return : 		The number of bytes read, negative means the read failed.
 */ 
static ssize_t device_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)  
{ 
	struct dev_info *p_dev_info = filp->private_data; 
	struct spi_message msg = {0};
	struct spi_transfer xfer = {0};
	unsigned char rxbuf[5];
	int ret;
	
	xfer.rx_buf = rxbuf;
	/* transfer 8 bits one time */
	xfer.bits_per_word = 8;
	xfer.len = cnt;
	
	spi_message_init(&msg);
	spi_message_add_tail(&xfer, &msg);
	/* sync transmission in block mode */
	ret = spi_sync(p_dev_info->spidev, &msg);
	if (ret)
		return ret;
	
	return copy_to_user(buf, rx, cnt);
} 

/* 
 * @description :	Write to the device
 * @param - filp :	Device file, indicating the opened file descriptor.
 * @param - buf :	The data to write to the device.
 * @param - cnt :	The length of the data to be written.
 * @param - offt :	Offset relative to the first address of the file.
 * @return : 		The number of bytes written, negative means the write failed.
 */ 
static ssize_t device_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 
{ 
	struct dev_info *p_dev_info = filp->private_data; 
	struct spi_message msg = {0};
	struct spi_transfer xfer = {0};
	unsigned char txbuf[5];
	
	copy_from_user(txbuf, buf, cnt);
	
	xfer.tx_buf = txbuf;
	/* transfer 8 bits one time */
	xfer.bits_per_word = 8;
	xfer.len = cnt;
	
	spi_message_init(&msg);
	spi_message_add_tail(&xfer, &msg);
	/* sync transmission in block mode */
	ret = spi_sync(p_dev_info->spidev, &msg);
	if (ret)
		return ret;
	
	return cnt; 
} 

/* 
 * @description :	Close/Release the device.
 * @param – inode :	The inode passed to the driver.
 * @param - filp :	The device file (file descriptor) to close.
 * @return :		0: Successful; Others: Failed.
 */ 
static int device_release(struct inode *inode, struct file *filp) 
{ 

	return 0; 
} 

/* The device operation function structure. */
static const struct file_operations device_fops = {
	.owner = THIS_MODULE,
	.open = device_open,
	.read = device_read,
	.write = device_write,
	.release = device_release,
}; 

/* 
 * @description :	Initialize the device.
 * @param -pdev:	Pointer to spi_device struct.
 * @return :		0: Successful; Others: Failed.
 */
static int device_init(struct spi_device *spidev) 
{ 
	
	return 0;
}

/* 
 * @description :	Probe function of the platform, it will be executed when the 
 * 					platform driver and platform device matching successfully.
 * @param -spidev :	Pointer to spi_device struct, means spi device.
 * @return :		0: Successful; Others: Failed.
 */
static int device_probe(struct spi_device *spidev)
{
	int ret;
	
	dev_info(&spidev->dev, "Driver and device matched successfully!\n");
	
	device_info.spidev = spidev;
	
	/* Store the dev_info pointer in spidev->dev.driver_data for later use */
	spi_set_drvdata(spidev, &device_info);
	
	/* Device init */
	ret = device_init(spidev);
	if (ret)
		return ret;

	/* Creat device id. */
	if (device_info.major) 
	{ 
		device_info.devid = MKDEV(device_info.major, 0); 
		ret = register_chrdev_region(device_info.devid, DEVICE_CNT, DEVICE_NAME); 
		if (ret) 
			goto out1; 
	} 
	else 
	{ 
		ret = alloc_chrdev_region(&device_info.devid, 0, DEVICE_CNT, DEVICE_NAME); 
		if (ret) 
			goto out1; 
	
		device_info.major = MAJOR(device_info.devid); 
		device_info.minor = MINOR(device_info.devid); 
	} 
	
	printk("%s: device id: major=%d, minor=%d\r\n", DEVICE_NAME, device_info.major, device_info.minor);
	
	/* Initialize char device. */
	device_info.cdev.owner = THIS_MODULE; 
	cdev_init(&device_info.cdev, &device_fops); 
	
	/* Add a char device. */
	ret = cdev_add(&device_info.cdev, device_info.devid, DEVICE_CNT); 
	if (ret) 
		goto out2; 
	
	/* Creat class. */
	device_info.class = class_create(THIS_MODULE, DEVICE_NAME); 
	if (IS_ERR(device_info.class)) 
	{ 
		ret = PTR_ERR(device_info.class); 
		goto out3; 
	}
	
	/* Creat device (node) file. */
	device_info.device = device_create(device_info.class, &client->dev, device_info.devid, NULL, DEVICE_NAME); 
	if (IS_ERR(device_info.device)) 
	{ 
		ret = PTR_ERR(device_info.device); 
		goto out4; 
	} 
 
	return 0;
 
out4:
	class_destroy(device_info.class); 
out3:
	cdev_del(&device_info.cdev);
out2:
	unregister_chrdev_region(device_info.devid, DEVICE_CNT); 
out1:
	/* Some code to release resources */
	
	return ret; 
} 

/* 
 * @description :	Release some resources. This function will be executed when the platform
 *					driver module is unloaded.
 * @param -spidev :	Pointer to spi_device struct, means spi device.
 * @return :		0: Successful; Others: Failed.
 */
static int device_remove(struct spi_device *spidev) 
{ 
	/* Logoff device (node) */
	device_destroy(device_info.class, device_info.devid); 
	
	/* Logoff class */ 
	class_destroy(device_info.class); 
	
	/* Delete char device */ 
	cdev_del(&device_info.cdev); 
	
	/* Logoff device id */ 
	unregister_chrdev_region(device_info.devid, DEVICE_CNT); 
	
	/* Some code to release resources */

	
	printk(KERN_INFO "%s: Driver removed!\n", DEVICE_NAME);

	return 0;
} 

/* Match table */
static const struct of_device_id device_of_match[] = {
	{.compatible = COMPAT_PROPT},
	{/* Sentinel */}
};

/* 
 * Declare device matching table. Note that this macro is generally used to dynamically 
 * load and unload drivers for hot-pluggable devices such as USB devices.
 */
MODULE_DEVICE_TABLE(of, device_of_match); 

/* i2c driver struct */
static struct spi_driver device_driver = {
	.driver = {
		.name = DEVICE_NAME,				//Drive name, used to match device who has the same name.
		.of_match_table = device_of_match,	//Used to match the device tree who has the same compatible property.
	},
	.probe = device_probe,					//probe function
	.remove = device_remove,				//remove function
};

/*
 * Register or unregister spi driver,
 * and Register the entry and exit functions of the Module.
 */
module_spi_driver(device_driver);

/* 
 *  Author, driver information and LICENSE.
 */ 
MODULE_AUTHOR("蒋楼丶");
MODULE_DESCRIPTION(DEVICE_NAME" Driver"); 
MODULE_LICENSE("GPL");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值