分类: linux设备驱动 linux应用层 2013-02-27 14:50 676人阅读 收藏 举报
一.spidev.c文件
看一个设备驱动的方法:
概览下重要的结构体spidev_data及全局变量device_list,bufsiz,SPIDEV_MAJOR...
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations--->spidev_fops
先看open方法spidev_open
接着看读写方法spidev_read & spidev_write
再接着看ioctl方法-->spidev_ioctl
再看其他剩余的方法
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/ioctl.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/list.h>
- #include <linux/errno.h>
- #include <linux/mutex.h>
- #include <linux/slab.h>
- #include <linux/spi/spi.h>
- #include <linux/spi/spidev.h>
- #include <asm/uaccess.h>
-
- #define SPIDEV_MAJOR 153 //spidev主设备号
- #define N_SPI_MINORS 32 /* ... up to 256 */
- static DECLARE_BITMAP(minors, N_SPI_MINORS);
- #define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)
-
- struct spidev_data {
- dev_t devt;
- spinlock_t spi_lock;
- struct spi_device *spi;
- struct list_head device_entry;
- struct mutex buf_lock;
- unsigned users;
- u8 *buffer;
- };
-
- static LIST_HEAD(device_list);
- static DEFINE_MUTEX(device_list_lock);
- static unsigned bufsiz = 4096;
- module_param(bufsiz, uint, S_IRUGO);
- MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
-
- static void spidev_complete(void *arg)
- {
- complete(arg);
- }
-
- static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- int status;
-
- message->complete = spidev_complete;
- message->context = &done;
-
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL)
- status = -ESHUTDOWN;
- else
- status = spi_async(spidev->spi, message);
- spin_unlock_irq(&spidev->spi_lock);
-
- if (status == 0) {
- wait_for_completion(&done);
- status = message->status;
- if (status == 0)
- status = message->actual_length;
- }
- return status;
- }
-
- static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len)
- {
- struct spi_transfer t = {
- .tx_buf = spidev->buffer,
- .len = len,
- };
- struct spi_message m;
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- return spidev_sync(spidev, &m);
- }
-
- static inline ssize_t spidev_sync_read(struct spidev_data *spidev, size_t len)
- {
- struct spi_transfer t = {
- .rx_buf = spidev->buffer,
- .len = len,
- };
- struct spi_message m;
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- return spidev_sync(spidev, &m);
- }
-
- static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
- {
- struct spidev_data *spidev;
- ssize_t status = 0;
-
- if (count > bufsiz)
- return -EMSGSIZE;
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
- status = spidev_sync_read(spidev, count);
- if (status > 0) {
- unsigned long missing;
- missing = copy_to_user(buf, spidev->buffer, status);
- if (missing == status)
- status = -EFAULT;
- else
- status = status - missing;
- }
- mutex_unlock(&spidev->buf_lock);
- return status;
- }
-
- static ssize_t spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
- {
- struct spidev_data *spidev;
- ssize_t status = 0;
- unsigned long missing;
-
- if (count > bufsiz)
- return -EMSGSIZE;
- spidev = filp->private_data;
- mutex_lock(&spidev->buf_lock);
- missing = copy_from_user(spidev->buffer, buf, count);
- if (missing == 0) {
- status = spidev_sync_write(spidev, count);
- }
- else
- status = -EFAULT;
- mutex_unlock(&spidev->buf_lock);
- return status;
- }
-
- static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
- {
- struct spi_message msg;
- struct spi_transfer *k_xfers;
- struct spi_transfer *k_tmp;
- struct spi_ioc_transfer *u_tmp;
- unsigned n, total;
- u8 *buf;
- int status = -EFAULT;
-
- spi_message_init(&msg);
- k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
- if (k_xfers == NULL)
- return -ENOMEM;
- buf = spidev->buffer;
- total = 0;
-
- for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {
- k_tmp->len = u_tmp->len;
- total += k_tmp->len;
- if (total > bufsiz) {
- status = -EMSGSIZE;
- goto done;
- }
- if (u_tmp->rx_buf) {
- k_tmp->rx_buf = buf;
- if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))
- goto done;
- }
- if (u_tmp->tx_buf) {
- k_tmp->tx_buf = buf;
- if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len))
- goto done;
- }
- buf += k_tmp->len;
- k_tmp->cs_change = !!u_tmp->cs_change;
- k_tmp->bits_per_word = u_tmp->bits_per_word;
- k_tmp->delay_usecs = u_tmp->delay_usecs;
- k_tmp->speed_hz = u_tmp->speed_hz;
- #ifdef VERBOSE
- dev_dbg(&spidev->spi->dev," xfer len %zd %s%s%s%dbits %u usec %uHz\n",
- u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",
- u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
- #endif
- spi_message_add_tail(k_tmp, &msg);
- }
-
- status = spidev_sync(spidev, &msg);
- if (status < 0)
- goto done;
- buf = spidev->buffer;
- for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
- if (u_tmp->rx_buf) {
- if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {
- status = -EFAULT;
- goto done;
- }
- }
- buf += u_tmp->len;
- }
- status = total;
- done:
- kfree(k_xfers);
- return status;
- }
-
- static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- int err = 0;
- int retval = 0;
- struct spidev_data *spidev;
- struct spi_device *spi;
- u32 tmp;
- unsigned n_ioc;
- struct spi_ioc_transfer *ioc;
-
- if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
- return -ENOTTY;
- 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;
-
- spidev = filp->private_data;
- spin_lock_irq(&spidev->spi_lock);
- spi = spi_dev_get(spidev->spi);
- spin_unlock_irq(&spidev->spi_lock);
- if (spi == NULL)
- return -ESHUTDOWN;
- mutex_lock(&spidev->buf_lock);
-
- switch (cmd) {
- 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;
- case SPI_IOC_WR_MODE:
- retval = __get_user(tmp, (u8 __user *)arg);
- if (retval == 0) {
- u8 save = spi->mode;
-
- if (tmp & ~SPI_MODE_MASK) {
- retval = -EINVAL;
- break;
- }
-
- tmp |= spi->mode & ~SPI_MODE_MASK;
- spi->mode = (u8)tmp;
- retval = spi_setup(spi);
- if (retval < 0)
- spi->mode = save;
- else
- dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
- }
- break;
- case SPI_IOC_WR_LSB_FIRST:
- retval = __get_user(tmp, (__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",tmp ? 'l' : 'm');
- }
- break;
- case SPI_IOC_WR_BITS_PER_WORD:
- retval = __get_user(tmp, (__u8 __user *)arg);
- if (retval == 0) {
- u8 save = spi->bits_per_word;
-
- spi->bits_per_word = tmp;
- retval = spi_setup(spi);
- if (retval < 0)
- spi->bits_per_word = save;
- else
- dev_dbg(&spi->dev, "%d bits per word\n", tmp);
- }
- break;
- case SPI_IOC_WR_MAX_SPEED_HZ:
- retval = __get_user(tmp, (__u32 __user *)arg);
- if (retval == 0) {
- u32 save = spi->max_speed_hz;
-
- spi->max_speed_hz = tmp;
- retval = spi_setup(spi);
- if (retval < 0)
- spi->max_speed_hz = save;
- else
- dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
- }
- break;
-
- default:
-
- if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {
- retval = -ENOTTY;
- break;
- }
-
- tmp = _IOC_SIZE(cmd);
- if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
- retval = -EINVAL;
- break;
- }
- n_ioc = tmp / sizeof(struct spi_ioc_transfer);
- if (n_ioc == 0)
- break;
-
- ioc = kmalloc(tmp, GFP_KERNEL);
- if (!ioc) {
- retval = -ENOMEM;
- break;
- }
- if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
- kfree(ioc);
- retval = -EFAULT;
- break;
- }
-
- retval = spidev_message(spidev, ioc, n_ioc);
- kfree(ioc);
- break;
- }
-
- mutex_unlock(&spidev->buf_lock);
- spi_dev_put(spi);
- return retval;
- }
-
- static int spidev_open(struct inode *inode, struct file *filp)
- {
- struct spidev_data *spidev;
- int status = -ENXIO;
-
- mutex_lock(&device_list_lock);
- list_for_each_entry(spidev, &device_list, device_entry) {
- if (spidev->devt == inode->i_rdev) {
- status = 0;
- break;
- }
- }
- if (status == 0) {
- if (!spidev->buffer) {
- spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
- if (!spidev->buffer) {
- dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
- status = -ENOMEM;
- }
- }
- if (status == 0) {
- spidev->users++;
- filp->private_data = spidev;
- nonseekable_open(inode, filp);
- }
- }
- else
- pr_debug("spidev: nothing for minor %d\n", iminor(inode));
- mutex_unlock(&device_list_lock);
- return status;
- }
-
- static int spidev_release(struct inode *inode, struct file *filp)
- {
- struct spidev_data *spidev;
- int status = 0;
-
- mutex_lock(&device_list_lock);
- spidev = filp->private_data;
- filp->private_data = NULL;
- spidev->users--;
- if (!spidev->users) {
- int dofree;
- kfree(spidev->buffer);
- spidev->buffer = NULL;
- spin_lock_irq(&spidev->spi_lock);
- dofree = (spidev->spi == NULL);
- spin_unlock_irq(&spidev->spi_lock);
- if (dofree)
- kfree(spidev);
- }
- mutex_unlock(&device_list_lock);
- return status;
- }
-
- static const struct file_operations spidev_fops = {
- .owner = THIS_MODULE,
- .write = spidev_write,
- .read = spidev_read,
- .unlocked_ioctl = spidev_ioctl,
- .open = spidev_open,
- .release = spidev_release,
- .llseek = no_llseek,
- };
-
- static struct class *spidev_class;
-
- static int __devinit spidev_probe(struct spi_device *spi)
- {
- struct spidev_data *spidev;
- int status;
- unsigned long minor;
-
- spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
- if (!spidev)
- return -ENOMEM;
- spidev->spi = spi;
- spin_lock_init(&spidev->spi_lock);
- mutex_init(&spidev->buf_lock);
- INIT_LIST_HEAD(&spidev->device_entry);
- mutex_lock(&device_list_lock);
- minor = find_first_zero_bit(minors, N_SPI_MINORS);
- if (minor < N_SPI_MINORS) {
- struct device *dev;
- spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
-
- dev = device_create(spidev_class, &spi->dev, spidev->devt,spidev, "spidev%d.%d",spi->master->bus_num, spi->chip_select);
- status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
- }
- else {
- dev_dbg(&spi->dev, "no minor number available!\n");
- status = -ENODEV;
- }
- if (status == 0) {
- set_bit(minor, minors);
- list_add(&spidev->device_entry, &device_list);
- }
- mutex_unlock(&device_list_lock);
-
- if (status == 0)
- spi_set_drvdata(spi, spidev);
- else
- kfree(spidev);
-
- return status;
- }
-
- static int __devexit spidev_remove(struct spi_device *spi)
- {
- struct spidev_data *spidev = spi_get_drvdata(spi);
- spin_lock_irq(&spidev->spi_lock);
- spidev->spi = NULL;
- spi_set_drvdata(spi, NULL);
- spin_unlock_irq(&spidev->spi_lock);
- mutex_lock(&device_list_lock);
- list_del(&spidev->device_entry);
- device_destroy(spidev_class, spidev->devt);
- clear_bit(MINOR(spidev->devt), minors);
- if (spidev->users == 0)
- kfree(spidev);
- mutex_unlock(&device_list_lock);
- return 0;
- }
-
- static struct spi_driver spidev_spi_driver = {
- .driver = {
- .name = "spidev",
- .owner = THIS_MODULE,
- },
- .probe = spidev_probe,
- .remove = __devexit_p(spidev_remove),
- };
-
- static int __init spidev_init(void)
- {
- int status;
- BUILD_BUG_ON(N_SPI_MINORS > 256);
-
- status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
- if (status < 0)
- return status;
- spidev_class = class_create(THIS_MODULE, "spidev");
- if (IS_ERR(spidev_class)) {
- unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
- return PTR_ERR(spidev_class);
- }
- status = spi_register_driver(&spidev_spi_driver);
- if (status < 0) {
- class_destroy(spidev_class);
- unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
- }
- return status;
- }
- module_init(spidev_init);
-
- static void __exit spidev_exit(void)
- {
- spi_unregister_driver(&spidev_spi_driver);
- class_destroy(spidev_class);
- unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
- }
- module_exit(spidev_exit);
-
- MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
- MODULE_DESCRIPTION("User mode SPI device interface");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("spi:spidev");
二.用户空间例子(spidev_test.c)
- #include <stdint.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <getopt.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #include <linux/types.h>
- #include <linux/spi/spidev.h>
-
- #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
- static void pabort(const char *s)
- {
- perror(s);
- abort();
- }
-
- static const char *device = "/dev/spidev1.1";
- static uint8_t mode;
- static uint8_t bits = 8;
- static uint32_t speed = 500000;
- static uint16_t delay;
-
- static void transfer(int fd)
- {
- int ret;
- uint8_t tx[] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
- 0xF0, 0x0D,
- };
- uint8_t rx[ARRAY_SIZE(tx)] = {0, };
- struct spi_ioc_transfer tr = {
- .tx_buf = (unsigned long)tx,
- .rx_buf = (unsigned long)rx,
- .len = ARRAY_SIZE(tx),
- .delay_usecs = delay,
- .speed_hz = speed,
- .bits_per_word = bits,
- };
-
- ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
- if (ret < 1)
- pabort("can't send spi message");
-
- for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
- if (!(ret % 6))
- puts("");
- printf("%.2X ", rx[ret]);
- }
- puts("");
- }
-
- static void print_usage(const char *prog)
- {
- printf("Usage: %s [-DsbdlHOLC3]\n", prog);
- puts(" -D --device device to use (default /dev/spidev1.1)\n"
- " -s --speed max speed (Hz)\n"
- " -d --delay delay (usec)\n"
- " -b --bpw bits per word \n"
- " -l --loop loopback\n"
- " -H --cpha clock phase\n"
- " -O --cpol clock polarity\n"
- " -L --lsb least significant bit first\n"
- " -C --cs-high chip select active high\n"
- " -3 --3wire SI/SO signals shared\n");
- exit(1);
- }
-
- static void parse_opts(int argc, char *argv[])
- {
- while (1) {
- static const struct option lopts[] = {
- { "device", 1, 0, 'D' },
- { "speed", 1, 0, 's' },
- { "delay", 1, 0, 'd' },
- { "bpw", 1, 0, 'b' },
- { "loop", 0, 0, 'l' },
- { "cpha", 0, 0, 'H' },
- { "cpol", 0, 0, 'O' },
- { "lsb", 0, 0, 'L' },
- { "cs-high", 0, 0, 'C' },
- { "3wire", 0, 0, '3' },
- { "no-cs", 0, 0, 'N' },
- { "ready", 0, 0, 'R' },
- { NULL, 0, 0, 0 },
- };
- int c;
-
- c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
-
- if (c == -1)
- break;
-
- switch (c) {
- case 'D':
- device = optarg;
- break;
- case 's':
- speed = atoi(optarg);
- break;
- case 'd':
- delay = atoi(optarg);
- break;
- case 'b':
- bits = atoi(optarg);
- break;
- case 'l':
- mode |= SPI_LOOP;
- break;
- case 'H':
- mode |= SPI_CPHA;
- break;
- case 'O':
- mode |= SPI_CPOL;
- break;
- case 'L':
- mode |= SPI_LSB_FIRST;
- break;
- case 'C':
- mode |= SPI_CS_HIGH;
- break;
- case '3':
- mode |= SPI_3WIRE;
- break;
- case 'N':
- mode |= SPI_NO_CS;
- break;
- case 'R':
- mode |= SPI_READY;
- break;
- default:
- print_usage(argv[0]);
- break;
- }
- }
- }
-
- int main(int argc, char *argv[])
- {
- int ret = 0;
- int fd;
-
- parse_opts(argc, argv);
-
- fd = open(device, O_RDWR);
- if (fd < 0)
- pabort("can't open device");
-
-
-
-
- ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
- if (ret == -1)
- pabort("can't set spi mode");
-
- ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
- if (ret == -1)
- pabort("can't get spi mode");
-
-
-
-
- ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
- if (ret == -1)
- pabort("can't set bits per word");
-
- ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
- if (ret == -1)
- pabort("can't get bits per word");
-
-
-
-
- ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
- if (ret == -1)
- pabort("can't set max speed hz");
-
- ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
- if (ret == -1)
- pabort("can't get max speed hz");
-
- printf("spi mode: %d\n", mode);
- printf("bits per word: %d\n", bits);
- printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
-
- transfer(fd);
-
- close(fd);
-
- return ret;
- }
这里整理下ioctl的命令:
- SPI_IOC_RD_MODE
- SPI_IOC_RD_LSB_FIRST
- SPI_IOC_RD_BITS_PER_WORD
- SPI_IOC_RD_MAX_SPEED_HZ
- SPI_IOC_WR_MODE
- SPI_IOC_WR_LSB_FIRST
- SPI_IOC_WR_BITS_PER_WORD
- SPI_IOC_WR_MAX_SPEED_HZ
- SPI_IOC_MESSAGE(n)
|