一.spidev.c文件
看一個設備驅動的方法:
module_init標識的入口初始化函數spidev_init,(module_exit標識的出口函數)
設備與設備驅動匹配時候調用的probe方法spidev_probe
設備驅動的操作函數集file_operations--->spidev_fops
@@open方法spidev_open
進行檢查, 重點是以后三條語句,其他的見下面代碼注釋:
- spidev->users++; //spidev_data使用者計數++
- filp->private_data = spidev; //spidev_data放在文件的私有數據里
- nonseekable_open(inode, filp); //設置文件的打開模式(文件讀寫指針不會跟隨讀寫操作移動)
@@read方法spidev_read
spidev = filp->private_data;=========>>status = spidev_sync_read(spidev, count);===========>>
spidev_sync(spidev, &m);==========>>status = spi_async(spidev->spi, message);===========>>
wait_for_completion(&done);========>>到了這一步是重點,在spi_async()方法中,使用以下語句將要做的事情加到workqueue中
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
此后所有的處理程序便轉移到在之前初始化的work方法中看以下代碼:
點擊(此處)折疊或打開
- static void bitbang_work(struct work_struct *work)
- {
- struct spi_bitbang *bitbang =
- container_of(work, struct spi_bitbang, work);
- unsigned long flags;
- int do_setup = -1;
- int (*setup_transfer)(struct spi_device *,
- struct spi_transfer *);
- setup_transfer = bitbang->setup_transfer;
- spin_lock_irqsave(&bitbang->lock, flags);
- bitbang->busy = 1;
- while (!list_empty(&bitbang->queue)) {
- struct spi_message *m;
- struct spi_device *spi;
- unsigned nsecs;
- struct spi_transfer *t = NULL;
- unsigned tmp;
- unsigned cs_change;
- int status;
- m = container_of(bitbang->queue.next, struct spi_message,
- queue);
- list_del_init(&m->queue);
- spin_unlock_irqrestore(&bitbang->lock, flags);
- /* FIXME this is made-up ... the correct value is known to
- * word-at-a-time bitbang code, and presumably chipselect()
- * should enforce these requirements too?
- */
- nsecs = 100;
- spi = m->spi;
- tmp = 0;
- cs_change = 1;
- status = 0;
- list_for_each_entry (t, &m->transfers, transfer_list) {
- /* override speed or wordsize? */
- if (t->speed_hz || t->bits_per_word)
- do_setup = 1;
- /* init (-1) or override (1) transfer params */
- if (do_setup != 0) {
- if (!setup_transfer) {
- status = -ENOPROTOOPT;
- break;
- }
- status = setup_transfer(spi, t);
- if (status < 0)
- break;
- }
- /* set up default clock polarity, and activate chip;
- * this implicitly updates clock and spi modes as
- * previously recorded for this device via setup().
- * (and also deselects any other chip that might be
- * selected ...)
- */
- if (cs_change) {
- bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;
- if (!t->tx_buf && !t->rx_buf && t->len) {
- status = -EINVAL;
- break;
- }
- /* transfer data. the lower level code handles any
- * new dma mappings it needs. our caller always gave
- * us dma-safe buffers.
- */
- if (t->len) {
- /* REVISIT dma API still needs a designated
- * DMA_ADDR_INVALID; ~0 might be better.
- */
- if (!m->is_dma_mapped)
- t->rx_dma = t->tx_dma = 0;
- status = bitbang->txrx_bufs(spi, t);
- }
- if (status > 0)
- m->actual_length += status;
- if (status != t->len) {
- /* always report some kind of error */
- if (status >= 0)
- status = -EREMOTEIO;
- break;
- }
- status = 0;
- /* protocol tweaks before next transfer */
- if (t->delay_usecs)
- udelay(t->delay_usecs);
- if (!cs_change)
- continue;
- if (t->transfer_list.next == &m->transfers)
- break;
- /* sometimes a short mid-message deselect of the chip
- * may be needed to terminate a mode or command
- */
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- m->status = status;
- m->complete(m->context);
- /* restore speed and wordsize if it was overridden */
- if (do_setup == 1)
- setup_transfer(spi, NULL);
- do_setup = 0;
- /* normally deactivate chipselect ... unless no error and
- * cs_change has hinted that the next message will probably
- * be for this chip too.
- */
- if (!(status == 0 && cs_change)) {
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- spin_lock_irqsave(&bitbang->lock, flags);
- }
- bitbang->busy = 0;
- spin_unlock_irqrestore(&bitbang->lock, flags);
- }
結束處理所有任務后,見上面紅色底紋部分解除wait_for_completion(&done);
最后missing = copy_to_user(buf, spidev->buffer, status);將數據發送到用戶空間
@@write方法spidev_write
與上面open方式基本相同
@@ioctl方法spidev_ioctl
具體的詳解見下面章節(三,四)
下面是spidev.c添加注釋部分
- #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; //spi設備結構體
- struct list_head device_entry;
- struct mutex buf_lock; //互斥鎖
- unsigned users; //使用者計數
- u8 *buffer; //緩沖區
- };
- static LIST_HEAD(device_list); //聲明spi設備鏈表
- 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); //調用complete
- }
- static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)
- {
- DECLARE_COMPLETION_ONSTACK(done);
- int status;
- message->complete = spidev_complete; //設置spi消息的complete方法 回調函數
- message->context = &done;
- spin_lock_irq(&spidev->spi_lock);
- if (spidev->spi == NULL) //判斷是否有指定對應的spi設備
- status = -ESHUTDOWN;
- else
- status = spi_async(spidev->spi, message); //spi異步同步
- spin_unlock_irq(&spidev->spi_lock);
- if (status == 0) {
- wait_for_completion(&done); //等待傳輸完成
- status = message->status; //獲取spi消息傳輸事務狀態
- if (status == 0)
- status = message->actual_length; //status等於傳輸的實際長度
- }
- 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消息(初始化spi傳遞事務隊列)
- spi_message_add_tail(&t, &m); //添加spr傳遞到該隊列
- 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消息(初始化spi傳遞事務隊列)
- spi_message_add_tail(&t, &m); //添加spr傳遞到該隊列
- 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; //從文件私有數據指針獲取spidev_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; //從文件私有數據指針獲取spidev_data
- mutex_lock(&spidev->buf_lock); //上互斥鎖
- missing = copy_from_user(spidev->buffer, buf, count); //用戶空間復制到內核空間
- if (missing == 0) { //傳輸失敗個數為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); //初始化spi消息(初始化spi傳遞事務隊列)
- k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); //分配spi傳輸指針內存
- if (k_xfers == NULL)
- return -ENOMEM;
- buf = spidev->buffer; //獲取spidev_data的緩沖區
- total = 0;
- //n=xfers為spi_ioc_transfer個數,u_tmp = u_xfers為要處理的spi_ioc_transfer指針
- 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) { //信息量超過bufsiz緩沖區最大容量
- status = -EMSGSIZE;
- goto done;
- }
- if (u_tmp->rx_buf) { //接收緩沖區指針不為空
- k_tmp->rx_buf = 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; //緩沖區指針指向buf
- if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len)) //用戶空間復制數據到buf
- goto done;
- }
- buf += k_tmp->len; //緩沖區指針移動一個傳輸信息的長度
- k_tmp->cs_change = !!u_tmp->cs_change; //設置cs_change
- k_tmp->bits_per_word = u_tmp->bits_per_word; //設置bits_per_word 一個字多少位
- k_tmp->delay_usecs = u_tmp->delay_usecs; //設置delay_usecs 毫秒級延時
- k_tmp->speed_hz = u_tmp->speed_hz; //設置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); //添加spr傳遞到該隊列
- }
- //for循環的作用是將spi_ioc_transfer批量轉換為spi傳遞結構體spi_transfer,然后添加進spi傳遞事務隊列
- status = spidev_sync(spidev, &msg); //同步讀寫
- if (status < 0)
- goto done;
- buf = spidev->buffer; //獲取spidev_data緩沖區指針
- for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { //批量從內核空間復制spi_ioc_transfer到用戶空間
- 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; //buf指針位置調整指向下一個spi_ioc_transfer
- }
- status = total; //status等於實際傳輸的數據長度
- done:
- kfree(k_xfers); //釋放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) //判斷控制命令的方向是否為讀read
- err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd)); //判斷傳輸數據大小
- if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) //判斷控制命令的方向是否為寫write
- err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd)); //判斷傳輸數據大小
- if (err)
- return -EFAULT;
- spidev = filp->private_data; //從文件私有數據中獲取spidev_data
- spin_lock_irq(&spidev->spi_lock); //上自旋鎖
- spi = spi_dev_get(spidev->spi); //獲取spi設備
- spin_unlock_irq(&spidev->spi_lock); //解自旋鎖
- if (spi == NULL) //獲取spi設備失敗
- return -ESHUTDOWN; //則返回錯誤
- mutex_lock(&spidev->buf_lock); //上互斥鎖
- switch (cmd) {
- case SPI_IOC_RD_MODE: //設置spi讀模式 (此處原作者的理解與我不同,這里應該是應用程序獲取數據)
- retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg);
- break;
- case SPI_IOC_RD_LSB_FIRST: //設置spi讀最低有效位 (此處原作者的理解與我不同,這里應該是應用程序獲取數據)
- retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,(__u8 __user *)arg);
- break;
- case SPI_IOC_RD_BITS_PER_WORD: //設置spi讀每個字含多個個位 (此處原作者的理解與我不同,這里應該是應用程序獲取數據)
- retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
- break;
- case SPI_IOC_RD_MAX_SPEED_HZ: //設置spi讀最大速率 (此處原作者的理解與我不同,這里應該是應用程序獲取數據)
- retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
- break;
- case SPI_IOC_WR_MODE: //設置spi寫模式
- retval = __get_user(tmp, (u8 __user *)arg);
- if (retval == 0) {
- u8 save = spi->mode; //獲取spi設備模式
- if (tmp & ~SPI_MODE_MASK) {
- retval = -EINVAL;
- break;
- }
- tmp |= spi->mode & ~SPI_MODE_MASK;
- spi->mode = (u8)tmp;
- retval = spi_setup(spi); //配置spi設備
- if (retval < 0)
- spi->mode = save;
- else
- dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
- }
- break;
- case SPI_IOC_WR_LSB_FIRST: //設置spi寫最低有效位
- retval = __get_user(tmp, (__u8 __user *)arg);
- if (retval == 0) {
- u8 save = spi->mode; //獲取spi設備模式
- if (tmp)
- spi->mode |= SPI_LSB_FIRST;
- else
- spi->mode &= ~SPI_LSB_FIRST;
- retval = spi_setup(spi); //配置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: //設置spi寫每個字含多個個位
- retval = __get_user(tmp, (__u8 __user *)arg); //用戶空間獲取數據
- if (retval == 0) {
- u8 save = spi->bits_per_word; //獲取spi設備 每個字含多少位
- spi->bits_per_word = tmp; //更新新的spi設備 每個字含多少位
- retval = spi_setup(spi); //配置spi設備
- if (retval < 0) //配置失敗
- spi->bits_per_word = save; //還原spi設備 每個字含多少位
- else
- dev_dbg(&spi->dev, "%d bits per word\n", tmp);
- }
- break;
- case SPI_IOC_WR_MAX_SPEED_HZ: //設置spi寫最大速率
- retval = __get_user(tmp, (__u32 __user *)arg); //用戶空間獲取數據
- if (retval == 0) {
- u32 save = spi->max_speed_hz; //獲取spi設備最大速率
- spi->max_speed_hz = tmp; //更新新的spi設備最大速率
- retval = spi_setup(spi); //配置spi設備
- if (retval < 0) //配置失敗
- spi->max_speed_hz = save; //還原spi設備最大速率
- else
- dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
- }
- break;
- default:
- //命令必須為寫方向的命令,且傳輸數據必須是SPI_IOC_MESSAGE()修飾的命令
- 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) { //判斷是否為spi_ioc_transfer對齊
- retval = -EINVAL;
- break;
- }
- n_ioc = tmp / sizeof(struct spi_ioc_transfer); //計算出spi_ioc_transfer數據的個數
- if (n_ioc == 0)
- break;
- ioc = kmalloc(tmp, GFP_KERNEL); //分配spi_ioc_transfer指針ioc內存
- if (!ioc) {
- retval = -ENOMEM;
- break;
- }
- if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //從用戶空間復制到內核空間
- kfree(ioc); //復制失敗則釋放ioc內存
- retval = -EFAULT;
- break;
- }
- retval = spidev_message(spidev, ioc, n_ioc); //spidev消息處理
- kfree(ioc); //釋放ioc內存
- break;
- }
- mutex_unlock(&spidev->buf_lock); //解互斥鎖
- spi_dev_put(spi); //增加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) { //遍歷device_list
- if (spidev->devt == inode->i_rdev) { //判斷設備號找到對應的設備
- status = 0; //設置狀態為0
- break;
- }
- }
- if (status == 0) { //找得到對應的設備
- if (!spidev->buffer) { //spidev_data緩沖區為空
- spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); //則分配內存
- if (!spidev->buffer) { //還空
- dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); //申請內存失敗
- status = -ENOMEM;
- }
- }
- if (status == 0) { //找得到對應的設備
- spidev->users++; //spidev_data使用者計數++
- filp->private_data = spidev; //spidev_data放在文件的私有數據里
- 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; //獲取spidev_data
- filp->private_data = NULL; //清除文件的私有數據指針
- spidev->users--; //使用者個數--
- if (!spidev->users) { //如果使用者個數為0
- int dofree;
- kfree(spidev->buffer); //釋放spidev_data的緩沖區內存
- spidev->buffer = NULL; //清除spidev_data緩沖區指針
- spin_lock_irq(&spidev->spi_lock); //上自旋鎖
- dofree = (spidev->spi == NULL); //判斷spi設備是否與spidev_data解綁了
- spin_unlock_irq(&spidev->spi_lock); //解自旋鎖
- if (dofree) //沒有捆綁的spi設備
- kfree(spidev); //則是否spidev_data內存
- }
- mutex_unlock(&device_list_lock);
- return status;
- }
- static const struct file_operations spidev_fops = { //文件操作函數集
- .owner = THIS_MODULE,
- .write = spidev_write, //寫write
- .read = spidev_read, //讀read
- .unlocked_ioctl = spidev_ioctl, //控制ioctl
- .open = spidev_open, //打開open
- .release = spidev_release, //釋放release
- .llseek = no_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); //分配spidev_data內存
- if (!spidev)
- return -ENOMEM;
- spidev->spi = spi; //設置spidev_data->spi(spi設備)
- spin_lock_init(&spidev->spi_lock);
- mutex_init(&spidev->buf_lock);
- INIT_LIST_HEAD(&spidev->device_entry); //初始化spidev_data入口鏈表
- 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/spidev%d.%d(spidev總線號.片選號)
- 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); //spi->dev->p->driver_data=spidev
- else
- kfree(spidev);
- return status;
- }
- static int __devexit spidev_remove(struct spi_device *spi)
- {
- struct spidev_data *spidev = spi_get_drvdata(spi); //根據spi設備獲取spidev_data
- spin_lock_irq(&spidev->spi_lock); //上自旋鎖
- spidev->spi = NULL; //清空spidev_data->spi指針
- spi_set_drvdata(spi, NULL); //spi->dev->p->driver_data=NULL
- spin_unlock_irq(&spidev->spi_lock); //解自旋鎖
- mutex_lock(&device_list_lock); //上互斥鎖
- list_del(&spidev->device_entry); //刪除spidev_data入口鏈表
- device_destroy(spidev_class, spidev->devt); //銷毀/dev/spidev%d.%d
- clear_bit(MINOR(spidev->devt), minors); //清除次設備位圖對應位
- if (spidev->users == 0) //使用者個數為0
- kfree(spidev); //釋放spidev_data內存
- mutex_unlock(&device_list_lock); //解互斥鎖
- return 0;
- }
- static struct spi_driver spidev_spi_driver = { //spi設備驅動
- .driver = {
- .name = "spidev",
- .owner = THIS_MODULE,
- },
- .probe = spidev_probe, //spidev的probe方法(當注冊了modalias域為"spidev"的spi設備或板級設備,則會調用probe方法)
- .remove = __devexit_p(spidev_remove), //spidev的remove方法
- };
- static int __init spidev_init(void) //spidev接口初始化
- {
- int status;
- BUILD_BUG_ON(N_SPI_MINORS > 256);
- //注冊字符設備,主設備號SPIDEV_MAJOR=153,捆綁的設備操作函數集為spidev_fops
- status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
- if (status < 0)
- return status;
- spidev_class = class_create(THIS_MODULE, "spidev"); //創建設備類spidev_class
- 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); //注冊spi設備驅動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) //spidev接口銷毀
- {
- spi_unregister_driver(&spidev_spi_driver); //注銷spi設備驅動spidev_spi_driver
- class_destroy(spidev_class); //注銷設備類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 = { //聲明並初始化spi_ioc_transfer結構體
- .tx_buf = (unsigned long)tx,
- .rx_buf = (unsigned long)rx,
- .len = ARRAY_SIZE(tx),
- .delay_usecs = delay,
- .speed_hz = speed,
- .bits_per_word = bits,
- };
- //SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的數量
- ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //ioctl默認操作,傳輸數據
- if (ret < 1)
- pabort("can't send spi message");
- for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收緩沖區
- if (!(ret % 6)) //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': //lsb 最低有效位
- mode |= SPI_LSB_FIRST;
- break;
- case 'C': //片選高電平
- mode |= SPI_CS_HIGH;
- break;
- case '3': //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");
- /*
- * spi mode //設置spi設備模式
- */
- 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");
- /*
- * bits per word //設置每個字含多少位
- */
- 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");
- /*
- * max speed hz //設置速率
- */
- 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 //讀 LSB
- SPI_IOC_RD_BITS_PER_WORD //讀 每字多少位
- SPI_IOC_RD_MAX_SPEED_HZ //讀 最大速率
- SPI_IOC_WR_MODE //寫 模式
- SPI_IOC_WR_LSB_FIRST //寫 LSB
- SPI_IOC_WR_BITS_PER_WORD //寫 每字多少位
- SPI_IOC_WR_MAX_SPEED_HZ //寫 最大速率
- SPI_IOC_MESSAGE(n) //傳輸n個數據包