Linux spi设备驱动

     Linux SPI驱动分主机驱动和设备驱动,主机驱动往往由厂商实现好,第三方开发员直接开发设备驱动即可。下面不阐述SPI子系统及驱动相关的架构,直接上一个SPI接口的温度传感器(TMP125)驱动来理解SPI驱动如何运作。这里也涉及Linux 驱动模型的知识。

   在板级文件增加SPI设备信息:

static struct omap2_mcspi_device_config ads7846_mcspi_config = {
	.turbo_mode	= 0,
	.single_channel	= 1,	/* 0: slave, 1: master */
};
static struct omap2_mcspi_device_config tmp125_mcspi_config = {
	.turbo_mode	= 0,
	.single_channel	= 1,	/* 0: slave, 1: master */
};
static struct spi_board_info omap3evm_spi_board_info[] = {
	[0] = {
		.modalias		= "ads7846",
		.bus_num		= 1,
		.chip_select		= 0,
		.max_speed_hz		= 1500000,
		.controller_data	= &ads7846_mcspi_config,
		.irq			= OMAP_GPIO_IRQ(OMAP3_EVM_TS_GPIO),
		.platform_data		= &ads7846_config,
	},
	[1] = {
		.modalias		= "tmp125",
		.bus_num		= 2,
		.chip_select		= 0,
		.max_speed_hz		= 2000000,
		.controller_data	= &tmp125_mcspi_config,
	},
};

这里需注意设备挂在CPU哪个SPI控制器上,及所在片选序号。这里笔者的TMP125挂在CPU的SPI2控制器上,片选为CS0。

在板级初始化函数中注册SPI设备信息:

	spi_register_board_info(omap3evm_spi_board_info,
				ARRAY_SIZE(omap3evm_spi_board_info));

实现SPI设备端的驱动:

tmp_driver.h文件

#ifndef TMP_DRIVER_H
#define TMP_DRIVER_H

#define LINUX2637_KERNEL	1
//#define MACR_DEBUG
#ifdef MACR_DEBUG
#define DEBUG_PRK(fmt,arg...)   printk(KERN_INFO fmt,##arg)
#else
#define DEBUG_PRK(fmt,arg...)   ;
#endif

#define MACR_DEV_NAME		"RFODNCC_TMP"


struct tmp125
{
	dev_t			devt;
	struct list_head	device_entry;
	char			phys[32];
	char			name[32];
	struct spi_device	*spi;
	struct regulator	*reg;
	struct spi_transfer	xfer;
	struct spi_message	msg;
	struct cdev dev;
	struct class *dev_class;
	spinlock_t spin_lock;
	struct mutex		buf_lock;
	const struct file_operations *fop;
	int major_num;
};


#define SPI_CPHA		0x01
#define SPI_CPOL		0x02

#define SPI_MODE_0		(0|0)
#define SPI_MODE_1		(0|SPI_CPHA)
#define SPI_MODE_2		(SPI_CPOL|0)
#define SPI_MODE_3		(SPI_CPOL|SPI_CPHA)

#define SPI_CS_HIGH		0x04
#define SPI_LSB_FIRST		0x08
#define SPI_3WIRE		0x10
#define SPI_LOOP		0x20
#define SPI_NO_CS		0x40
#define SPI_READY		0x80


#define SPI_MODE_MASK		(SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
				| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
				| SPI_NO_CS | SPI_READY)


/* IOCTL commands */

#define SPI_IOC_MAGIC			'k'

/* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
#define SPI_MSGSIZE(N) \
	((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
		? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])


/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) */
#define SPI_IOC_RD_MODE			_IOR(SPI_IOC_MAGIC, 1, __u8)
#define SPI_IOC_WR_MODE			_IOW(SPI_IOC_MAGIC, 1, __u8)

/* Read / Write SPI bit justification */
#define SPI_IOC_RD_LSB_FIRST		_IOR(SPI_IOC_MAGIC, 2, __u8)
#define SPI_IOC_WR_LSB_FIRST		_IOW(SPI_IOC_MAGIC, 2, __u8)

/* Read / Write SPI device word length (1..N) */
#define SPI_IOC_RD_BITS_PER_WORD	_IOR(SPI_IOC_MAGIC, 3, __u8)
#define SPI_IOC_WR_BITS_PER_WORD	_IOW(SPI_IOC_MAGIC, 3, __u8)

/* Read / Write SPI device default max speed hz */
#define SPI_IOC_RD_MAX_SPEED_HZ		_IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ		_IOW(SPI_IOC_MAGIC, 4, __u32)



#endif

tmp_driver.c文件


#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/netfilter.h>
#include <asm/page.h>
#include <linux/device.h>//for udev
#include <linux/slab.h>
//#include <asm/semaphore.h>//for semaphore in kernal version <2.6.26
#include <linux/semaphore.h> //for semaphore in kernal version 2.6.26 and above
#include <linux/spi/spi.h>
#include "tmp_driver.h"
         
static LIST_HEAD(device_list);
static void tmp_setup_spi_msg(struct tmp125 *tmp,unsigned char *buf,size_t len,char dir )
{
	struct spi_device	*spi;
	spi = spi_dev_get(tmp->spi);
	/* write operation */
	if(dir == 1)
	{
		spi_write(spi,buf,len);
	}
	/* read operation */
	else if(dir == 0)
	{
		spi_read(spi,buf,len);
	}
}

static int device_read(struct file *filp, char __user *buf, size_t size,
  loff_t *ppos)
{
	int ret = 0;
	struct tmp125 *tmp ;
	char __user *buffer;
	DEBUG_PRK("tmp125 device execute read operation!\n");

	buffer=kzalloc(size, GFP_KERNEL);
	if (!buffer)
	{
	  DEBUG_PRK("malloc memory error during read\n");
	  ret = -EFAULT;
	  goto err;
	}
	tmp = filp->private_data;
	mutex_lock(&tmp->buf_lock);
	tmp_setup_spi_msg(tmp,buffer,size,0);

	if(copy_to_user(buf,buffer,size))
	{
		DEBUG_PRK("copy to user error\n");
		ret = -EFAULT;
		goto err;
	}
	 ret=size;
	 mutex_unlock(&tmp->buf_lock);

err:
	kfree(buffer);
	return ret;
}

static int device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int			err = 0;
	int			retval = 0;
	struct tmp125 *tmp ;
	struct spi_device	*spi;
	u32			temp;
	//unsigned		n_ioc;
	//struct spi_ioc_transfer	*ioc;

	/* Check type and command number */
	if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
		return -ENOTTY;

	/* Check access direction once here; don't repeat below.
	 * IOC_DIR is from the user perspective, while access_ok is
	 * from the kernel perspective; so they look reversed.
	 */
	if (_IOC_DIR(cmd) & _IOC_READ)
		err = !access_ok(VERIFY_WRITE,
				(void __user *)arg, _IOC_SIZE(cmd));
	if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
		err = !access_ok(VERIFY_READ,
				(void __user *)arg, _IOC_SIZE(cmd));
	if (err)
		return -EFAULT;

	/* guard against device removal before, or while,
	 * we issue this ioctl.
	 */
	tmp = filp->private_data;
	spin_lock_irq(&tmp->spin_lock);
	spi = spi_dev_get(tmp->spi);
	spin_unlock_irq(&tmp->spin_lock);

	if (spi == NULL)
		return -ESHUTDOWN;

	/* use the buffer lock here for triple duty:
	 *  - prevent I/O (from us) so calling spi_setup() is safe;
	 *  - prevent concurrent SPI_IOC_WR_* from morphing
	 *    data fields while SPI_IOC_RD_* reads them;
	 *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
	 */
	mutex_lock(&tmp->buf_lock);

	switch (cmd) {
	/* read requests */
	case SPI_IOC_RD_MODE:
		retval = __put_user(spi->mode & SPI_MODE_MASK,
					(__u8 __user *)arg);
		break;
	case SPI_IOC_RD_LSB_FIRST:
		retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,
					(__u8 __user *)arg);
		break;
	case SPI_IOC_RD_BITS_PER_WORD:
		retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
		break;
	case SPI_IOC_RD_MAX_SPEED_HZ:
		retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
		break;

	/* write requests */
	case SPI_IOC_WR_MODE:
		retval = __get_user(temp, (u8 __user *)arg);
		if (retval == 0) {
			u8	save = spi->mode;

			if (temp & ~SPI_MODE_MASK) {
				retval = -EINVAL;
				break;
			}

			temp |= spi->mode & ~SPI_MODE_MASK;
			spi->mode = (u8)temp;
			retval = spi_setup(spi);
			if (retval < 0)
				spi->mode = save;
			else
				dev_dbg(&spi->dev, "spi mode %02x\n", temp);
		}
		break;
	case SPI_IOC_WR_LSB_FIRST:
		retval = __get_user(temp, (__u8 __user *)arg);
		if (retval == 0) {
			u8	save = spi->mode;

			if (tmp)
				spi->mode |= SPI_LSB_FIRST;
			else
				spi->mode &= ~SPI_LSB_FIRST;
			retval = spi_setup(spi);
			if (retval < 0)
				spi->mode = save;
			else
				dev_dbg(&spi->dev, "%csb first\n",
						temp ? 'l' : 'm');
		}
		break;
	case SPI_IOC_WR_BITS_PER_WORD:
		retval = __get_user(temp, (__u8 __user *)arg);
		if (retval == 0) {
			u8	save = spi->bits_per_word;

			spi->bits_per_word = temp;
			retval = spi_setup(spi);
			if (retval < 0)
				spi->bits_per_word = save;
			else
				dev_dbg(&spi->dev, "%d bits per word\n", temp);
		}
		break;
	case SPI_IOC_WR_MAX_SPEED_HZ:
		retval = __get_user(temp, (__u32 __user *)arg);
		if (retval == 0) {
			u32	save = spi->max_speed_hz;

			spi->max_speed_hz = temp;
			retval = spi_setup(spi);
			if (retval < 0)
				spi->max_speed_hz = save;
			else
				dev_dbg(&spi->dev, "%d Hz (max)\n", temp);
		}
		break;

	default:
		/* segmented and/or full-duplex I/O request */
		/* copy into scratch area */
		break;
	}

	mutex_unlock(&tmp->buf_lock);
	spi_dev_put(spi);
	return retval;
}


static int device_open(struct inode *inode, struct file *filp)
{
	struct tmp125 *tmp;
	list_for_each_entry(tmp, &device_list, device_entry)
	{
		if (tmp->devt == inode->i_rdev)
		{
			DEBUG_PRK("search device match!\n");
			filp->private_data = tmp;
			break;
		}
		else
			DEBUG_PRK("search dismatch\n");
	}
	DEBUG_PRK("tmp125 device was open!\n");
	return 0;
}
static int device_release(struct inode *inode, struct file *filp)
{

  DEBUG_PRK("tmp125 device was close!\n");
  return 0;
}

static const struct file_operations dev_fops =
{
  .owner =   THIS_MODULE,
#ifdef LINUX2637_KERNEL
  .unlocked_ioctl= device_ioctl,   //kernel version2.6.37
#else
  .ioctl =   device_ioctl,   	  //kernel version2.6.32
#endif
  .read =		device_read,
  .open =  	device_open,
  .release = device_release,
};

static void setup_cdev(struct tmp125 *tmp, int index)
{
   int err, devno = MKDEV(tmp->major_num, index);
   cdev_init(&tmp->dev, &dev_fops);
   tmp->dev.owner = THIS_MODULE;
   tmp->dev.ops = tmp->fop;
   err = cdev_add(&tmp->dev, devno, 1);
   if(err)
   {
      DEBUG_PRK("Error %d adding device %d", err, index);
   }
}


static int dev_init(struct tmp125 *tmp)
{
  int result;
  //dev_t devno;
  /* device init */
  result = alloc_chrdev_region(&tmp->devt, 0, 1,MACR_DEV_NAME);
  tmp->major_num= MAJOR(tmp->devt);
  if (result < 0)
  {
    return result;
  }
  setup_cdev(tmp, 0);
  /* auto create device node */
  tmp->dev_class = class_create(THIS_MODULE, MACR_DEV_NAME);
  if(IS_ERR(tmp->dev_class))
  {
    DEBUG_PRK("failed to create class when auto create device node!\n");
    cdev_del(&tmp->dev);
    unregister_chrdev_region(MKDEV(tmp->major_num, 0), 1);
    return 0;
  }
  device_create(tmp->dev_class, NULL, MKDEV(tmp->major_num, 0),NULL,MACR_DEV_NAME);
  /* spin lock init */
  spin_lock_init(&tmp->spin_lock);
  mutex_init(&tmp->buf_lock);
  DEBUG_PRK("module insmod success!\n");
  return 0;
}


static int tmp125_suspend(struct spi_device *spi, pm_message_t message)
{
	DEBUG_PRK("tmp125 device suspend successfully!\n");
	return 0;
}

static int tmp125_resume(struct spi_device *spi)
{
	DEBUG_PRK("tmp125 device resume successfully!\n");
	return 0;
}

static int  tmp125_probe(struct spi_device *spi)
{
	int err = 0;
	struct tmp125 *tmp;

	/* setup spi default attr*/
	spi->bits_per_word = 16;
	spi->mode = SPI_MODE_0;
	err = spi_setup(spi);
	if (err < 0)
	{
		return err;
	}
	/* setup tmp125 device */
	tmp = kzalloc(sizeof(struct tmp125), GFP_KERNEL);
	if (!tmp)
	{
		err = -ENOMEM;
		goto err_free_mem;
	}
	tmp->spi = spi;
	tmp->fop = &dev_fops;
	snprintf(tmp->phys, sizeof(tmp->phys), "%s/tmp125", dev_name(&spi->dev));
	snprintf(tmp->name, sizeof(tmp->name), "TMP125 Temperature Sensor");
	/* setup char device relative info*/
	dev_init(tmp);
	INIT_LIST_HEAD(&tmp->device_entry);
	list_add(&tmp->device_entry, &device_list);
	dev_set_drvdata(&spi->dev, tmp);
	DEBUG_PRK("tmp125 device probe successfully!\n");
	return 0;
err_free_mem:
	kfree(tmp);

	return err;
}


static int tmp125_remove(struct spi_device *spi)
{
	struct tmp125 *tmp = dev_get_drvdata(&spi->dev);

	DEBUG_PRK("tmp125 device remove successfully!\n");
	/* make sure ops on existing fds can abort cleanly */
	spin_lock_irq(&tmp->spin_lock);
	tmp->spi = NULL;
	spi_set_drvdata(spi, NULL);
	spin_unlock_irq(&tmp->spin_lock);
	list_del(&tmp->device_entry);
	/* prevent new opens */
	cdev_del(&tmp->dev);
	device_destroy(tmp->dev_class, MKDEV(tmp->major_num, 0));
   	class_destroy(tmp->dev_class);
	unregister_chrdev_region(MKDEV(tmp->major_num, 0), 1);
	kfree(tmp);
	return 0;
}


static struct spi_driver tmp125_driver = {
	.driver =
	{
		.name	= "tmp125",
		.bus	= &spi_bus_type,
		.owner	= THIS_MODULE,
	},
	.probe		= tmp125_probe,
	.remove	= __devexit_p(tmp125_remove),
	.suspend	= tmp125_suspend,
	.resume	= tmp125_resume,
};

static int  tmp125_init(void)
{
	DEBUG_PRK("tmp125_driver register successfully!\n");
	return spi_register_driver(&tmp125_driver);
}
module_init(tmp125_init);

static void  tmp125_exit(void)
{
	DEBUG_PRK("tmp125 driver unregister successfully!\n");
	spi_unregister_driver(&tmp125_driver);
}
module_exit(tmp125_exit);

MODULE_AUTHOR("Vincent Wu");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Temperature Sensor Driver");
MODULE_ALIAS("spi:tmp125");

这里需注意,SPI数据的传输是通过内核调用spi_write和spi_read实现。另外这个驱动是一个字符型驱动,实现一个重要的ioctl系统调用,用于用户在用户层空间实现SPI模式、传输波特率、数据为长度(tmp125的spi传输数据位长度为16位)等控制。


下面在是用户空间编程,获取tmp125的温度。

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <linux/spi/spidev.h>
#define MACR_RFODNCC_LED_NODE 		"/dev/RFODNCC_TMP"

static void setup_spi_read_param(int fd,int mode,int data_bit,int speed)

{
	int ret;
	int mode_s = mode;
	int data_bit_s =data_bit;
	int speed_s = speed;

	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode_s);
	if (ret == -1)
		printf("can't set spi mode");
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &data_bit_s);
	if (ret == -1)
		printf("can't set bits per word");
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_s);
	if (ret == -1)
		printf("can't get max speed hz");
	printf("---------------------------------------------------\n");
	printf("Temperature SPI mode 		: %d\n", mode_s);
	printf("Temperature SPI bits 		: %d\n", data_bit_s);
	printf("Temperature SPI current speed	: %d Hz\n", speed_s);
	printf("Temperature SPI max speed	: 2000000 Hz\n");
	printf("---------------------------------------------------\n");
}

int main(int argc, char *argv[])
{
  int i;
  int fd = 0;
  char buf[64];
  char len = 0;

  signed short SPI_Read;
  signed short Step1 ,Step2,Step3;
  signed short temperature16 ;
  fd = open(MACR_RFODNCC_LED_NODE,O_RDWR, O_SYNC);
  if(-1 == fd)
  {
      printf("Open file failed\n");
      return -1;
  }
  setup_spi_read_param(fd,0,16,1000000);
while(1)
{
   len=read(fd,buf,2);
   if(len)
   {
	   SPI_Read = buf[0]+(buf[1]<<8);
	   Step1 = (SPI_Read >> 5);
	   Step2 = Step1&0x0200;
	   if(Step2 == 0x0200)
	   {
		   Step3 = Step1|0xFE00;
	   }
	   else
	   {
		   Step3 = Step1&0x03FF;
	   }
	   temperature16 = Step3;
	   temperature16 = temperature16/4;
	   printf("Temperature : %d'C [SPI data :0x%04X]\n",temperature16,SPI_Read);
   }
   sleep(1);
}
  return 0; 
}


结束。



你可能感兴趣的:(Linux spi设备驱动)