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, }, };
在板级初始化函数中注册SPI设备信息:
spi_register_board_info(omap3evm_spi_board_info, ARRAY_SIZE(omap3evm_spi_board_info));
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
#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");
下面在是用户空间编程,获取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; }