轉(zhuǎn)自 http://www.cnblogs.com/sankye/p/3955630.html
一.spidev.c文件
看一個設(shè)備驅(qū)動的方法:
module_init標(biāo)識的入口初始化函數(shù)spidev_init,(module_exit標(biāo)識的出口函數(shù))
設(shè)備與設(shè)備驅(qū)動匹配時候調(diào)用的PRobe方法spidev_probe
設(shè)備驅(qū)動的操作函數(shù)集file_Operations--->spidev_fops
@@open方法spidev_open進(jìn)行檢查, 重點是以后三條語句,其他的見下面代碼注釋:
spidev->users++; //spidev_data使用者計數(shù)++ filp->private_data = spidev; //spidev_data放在文件的私有數(shù)據(jù)里 nonseekable_open(inode, filp); //設(shè)置文件的打開模式(文件讀寫指針不會跟隨讀寫操作移動)
@@read方法spidev_readspidev = 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中l(wèi)ist_add_tail(&m->queue, &bitbang->queue);queue_work(bitbang->workqueue, &bitbang->work);此后所有的處理程序便轉(zhuǎn)移到在之前初始化的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);}結(jié)束處理所有任務(wù)后,見上面紅色底紋部分解除wait_for_completion(&done);最后missing = copy_to_user(buf, spidev->buffer, status);將數(shù)據(jù)發(fā)送到用戶空間@@write方法spidev_write與上面open方式基本相同@@ioctl方法spidev_ioctl具體的詳解見下面章節(jié)(三,四)下面是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主設(shè)備號 #define N_SPI_MINORS 32 /* ... up to 256 */ static DECLARE_BITMAP(minors, N_SPI_MINORS); //聲明次設(shè)備位圖 #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; //設(shè)備號 spinlock_t spi_lock; //自旋鎖 struct spi_device *spi; //spi設(shè)備結(jié)構(gòu)體 struct list_head device_entry; struct mutex buf_lock; //互斥鎖 unsigned users; //使用者計數(shù) u8 *buffer; //緩沖區(qū) }; static LIST_HEAD(device_list); //聲明spi設(shè)備鏈表 static DEFINE_MUTEX(device_list_lock); //定義互斥鎖 static unsigned bufsiz = 4096; //最大傳輸緩沖區(qū)大小 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); //調(diào)用complete } static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; message->complete = spidev_complete; //設(shè)置spi消息的complete方法 回調(diào)函數(shù) message->context = &done; spin_lock_irq(&spidev->spi_lock); if (spidev->spi == NULL) //判斷是否有指定對應(yīng)的spi設(shè)備 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消息傳輸事務(wù)狀態(tài) if (status == 0) status = message->actual_length; //status等于傳輸?shù)膶嶋H長度 } return status; //返回實際傳輸長度 } static inline ssize_t spidev_sync_write(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { .tx_buf = spidev->buffer, //發(fā)送緩沖區(qū) .len = len, //發(fā)送數(shù)據(jù)長度 }; struct spi_message m; spi_message_init(&m); //初始化spi消息(初始化spi傳遞事務(wù)隊列) 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, //接收緩沖區(qū) .len = len, //接收數(shù)據(jù)長度 }; struct spi_message m; spi_message_init(&m); //初始化spi消息(初始化spi傳遞事務(wù)隊列) 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) //傳輸數(shù)據(jù)大于緩沖區(qū)容量 return -EMSGSIZE; spidev = filp->private_data; //從文件私有數(shù)據(jù)指針獲取spidev_data mutex_lock(&spidev->buf_lock); //上互斥鎖 status = spidev_sync_read(spidev, count); //同步讀,返回傳輸數(shù)據(jù)長度 if (status > 0) { unsigned long missing; //丟失的數(shù)據(jù)個數(shù) missing = copy_to_user(buf, spidev->buffer, status); //內(nèi)核空間復(fù)制到用戶空間 if (missing == status) //丟失的數(shù)據(jù)個數(shù)等于要傳輸?shù)臄?shù)據(jù)個數(shù) status = -EFAULT; else status = status - missing; //傳輸成功的數(shù)據(jù)個數(shù) } mutex_unlock(&spidev->buf_lock);//解互斥鎖 return status; //返回讀取成功的數(shù)據(jù)個數(shù) } 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) //傳輸數(shù)據(jù)大于緩沖區(qū)容量 return -EMSGSIZE; spidev = filp->private_data; //從文件私有數(shù)據(jù)指針獲取spidev_data mutex_lock(&spidev->buf_lock); //上互斥鎖 missing = copy_from_user(spidev->buffer, buf, count); //用戶空間復(fù)制到內(nèi)核空間 if (missing == 0) { //傳輸失敗個數(shù)為0 status = spidev_sync_write(spidev, count); //同步寫,返回傳輸數(shù)據(jù)長度 } else status = -EFAULT; mutex_unlock(&spidev->buf_lock);//解互斥鎖 return status; //返回寫數(shù)據(jù)的實際個數(shù) } 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傳遞事務(wù)隊列) k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); //分配spi傳輸指針內(nèi)存 if (k_xfers == NULL) return -ENOMEM; buf = spidev->buffer; //獲取spidev_data的緩沖區(qū) total = 0; //n=xfers為spi_ioc_transfer個數(shù),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; //設(shè)置傳輸信息的長度 total += k_tmp->len; //累加傳輸信息的總長度 if (total > bufsiz) { //信息量超過bufsiz緩沖區(qū)最大容量 status = -EMSGSIZE; goto done; } if (u_tmp->rx_buf) { //接收緩沖區(qū)指針不為空 k_tmp->rx_buf = buf; //緩沖區(qū)指向buf if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len)) goto done; } if (u_tmp->tx_buf) { //發(fā)送緩沖區(qū)指針不為空 k_tmp->tx_buf = buf; //緩沖區(qū)指針指向buf if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len)) //用戶空間復(fù)制數(shù)據(jù)到buf goto done; } buf += k_tmp->len; //緩沖區(qū)指針移動一個傳輸信息的長度 k_tmp->cs_change = !!u_tmp->cs_change; //設(shè)置cs_change k_tmp->bits_per_word = u_tmp->bits_per_word; //設(shè)置bits_per_word 一個字多少位 k_tmp->delay_usecs = u_tmp->delay_usecs; //設(shè)置delay_usecs 毫秒級延時 k_tmp->speed_hz = u_tmp->speed_hz; //設(shè)置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循環(huán)的作用是將spi_ioc_transfer批量轉(zhuǎn)換為spi傳遞結(jié)構(gòu)體spi_transfer,然后添加進(jìn)spi傳遞事務(wù)隊列 status = spidev_sync(spidev, &msg); //同步讀寫 if (status < 0) goto done; buf = spidev->buffer; //獲取spidev_data緩沖區(qū)指針 for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { //批量從內(nèi)核空間復(fù)制spi_ioc_transfer到用戶空間 if (u_tmp->rx_buf) { //判斷是否存在接收緩沖區(qū) 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指針位置調(diào)整指向下一個spi_ioc_transfer } status = total; //status等于實際傳輸?shù)臄?shù)據(jù)長度 done: kfree(k_xfers); //釋放k_xfers return status; //返回實際傳輸?shù)臄?shù)據(jù)長度 } 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)); //判斷傳輸數(shù)據(jù)大小 if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) //判斷控制命令的方向是否為寫write err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd)); //判斷傳輸數(shù)據(jù)大小 if (err) return -EFAULT; spidev = filp->private_data; //從文件私有數(shù)據(jù)中獲取spidev_data spin_lock_irq(&spidev->spi_lock); //上自旋鎖 spi = spi_dev_get(spidev->spi); //獲取spi設(shè)備 spin_unlock_irq(&spidev->spi_lock); //解自旋鎖 if (spi == NULL) //獲取spi設(shè)備失敗 return -ESHUTDOWN; //則返回錯誤 mutex_lock(&spidev->buf_lock); //上互斥鎖 switch (cmd) { case SPI_IOC_RD_MODE: //設(shè)置spi讀模式 (此處原作者的理解與我不同,這里應(yīng)該是應(yīng)用程序獲取數(shù)據(jù)) retval = __put_user(spi->mode & SPI_MODE_MASK,(__u8 __user *)arg); break; case SPI_IOC_RD_LSB_FIRST: //設(shè)置spi讀最低有效位 (此處原作者的理解與我不同,這里應(yīng)該是應(yīng)用程序獲取數(shù)據(jù)) retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,(__u8 __user *)arg); break; case SPI_IOC_RD_BITS_PER_WORD: //設(shè)置spi讀每個字含多個個位 (此處原作者的理解與我不同,這里應(yīng)該是應(yīng)用程序獲取數(shù)據(jù)) retval = __put_user(spi->bits_per_word, (__u8 __user *)arg); break; case SPI_IOC_RD_MAX_SPEED_HZ: //設(shè)置spi讀最大速率 (此處原作者的理解與我不同,這里應(yīng)該是應(yīng)用程序獲取數(shù)據(jù)) retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg); break; case SPI_IOC_WR_MODE: //設(shè)置spi寫模式 retval = __get_user(tmp, (u8 __user *)arg); if (retval == 0) { u8 save = spi->mode; //獲取spi設(shè)備模式 if (tmp & ~SPI_MODE_MASK) { retval = -EINVAL; break; } tmp |= spi->mode & ~SPI_MODE_MASK; spi->mode = (u8)tmp; retval = spi_setup(spi); //配置spi設(shè)備 if (retval < 0) spi->mode = save; else dev_dbg(&spi->dev, "spi mode %02x/n", tmp); } break; case SPI_IOC_WR_LSB_FIRST: //設(shè)置spi寫最低有效位 retval = __get_user(tmp, (__u8 __user *)arg); if (retval == 0) { u8 save = spi->mode; //獲取spi設(shè)備模式 if (tmp) spi->mode |= SPI_LSB_FIRST; else spi->mode &= ~SPI_LSB_FIRST; retval = spi_setup(spi); //配置spi設(shè)備 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: //設(shè)置spi寫每個字含多個個位 retval = __get_user(tmp, (__u8 __user *)arg); //用戶空間獲取數(shù)據(jù) if (retval == 0) { u8 save = spi->bits_per_word; //獲取spi設(shè)備 每個字含多少位 spi->bits_per_word = tmp; //更新新的spi設(shè)備 每個字含多少位 retval = spi_setup(spi); //配置spi設(shè)備 if (retval < 0) //配置失敗 spi->bits_per_word = save; //還原spi設(shè)備 每個字含多少位 else dev_dbg(&spi->dev, "%d bits per word/n", tmp); } break; case SPI_IOC_WR_MAX_SPEED_HZ: //設(shè)置spi寫最大速率 retval = __get_user(tmp, (__u32 __user *)arg); //用戶空間獲取數(shù)據(jù) if (retval == 0) { u32 save = spi->max_speed_hz; //獲取spi設(shè)備最大速率 spi->max_speed_hz = tmp; //更新新的spi設(shè)備最大速率 retval = spi_setup(spi); //配置spi設(shè)備 if (retval < 0) //配置失敗 spi->max_speed_hz = save; //還原spi設(shè)備最大速率 else dev_dbg(&spi->dev, "%d Hz (max)/n", tmp); } break; default: //命令必須為寫方向的命令,且傳輸數(shù)據(jù)必須是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); //計算傳輸數(shù)據(jù)大小 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數(shù)據(jù)的個數(shù) if (n_ioc == 0) break; ioc = kmalloc(tmp, GFP_KERNEL); //分配spi_ioc_transfer指針ioc內(nèi)存 if (!ioc) { retval = -ENOMEM; break; } if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //從用戶空間復(fù)制到內(nèi)核空間 kfree(ioc); //復(fù)制失敗則釋放ioc內(nèi)存 retval = -EFAULT; break; } retval = spidev_message(spidev, ioc, n_ioc); //spidev消息處理 kfree(ioc); //釋放ioc內(nèi)存 break; } mutex_unlock(&spidev->buf_lock); //解互斥鎖 spi_dev_put(spi); //增加spi設(shè)備的引用計數(shù) 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) { //判斷設(shè)備號找到對應(yīng)的設(shè)備 status = 0; //設(shè)置狀態(tài)為0 break; } } if (status == 0) { //找得到對應(yīng)的設(shè)備 if (!spidev->buffer) { //spidev_data緩沖區(qū)為空 spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); //則分配內(nèi)存 if (!spidev->buffer) { //還空 dev_dbg(&spidev->spi->dev, "open/ENOMEM/n"); //申請內(nèi)存失敗 status = -ENOMEM; } } if (status == 0) { //找得到對應(yīng)的設(shè)備 spidev->users++; //spidev_data使用者計數(shù)++ filp->private_data = spidev; //spidev_data放在文件的私有數(shù)據(jù)里 nonseekable_open(inode, filp); //設(shè)置文件的打開模式(文件讀寫指針不會跟隨讀寫操作移動) } } 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; //清除文件的私有數(shù)據(jù)指針 spidev->users--; //使用者個數(shù)-- if (!spidev->users) { //如果使用者個數(shù)為0 int dofree; kfree(spidev->buffer); //釋放spidev_data的緩沖區(qū)內(nèi)存 spidev->buffer = NULL; //清除spidev_data緩沖區(qū)指針 spin_lock_irq(&spidev->spi_lock); //上自旋鎖 dofree = (spidev->spi == NULL); //判斷spi設(shè)備是否與spidev_data解綁了 spin_unlock_irq(&spidev->spi_lock); //解自旋鎖 if (dofree) //沒有捆綁的spi設(shè)備 kfree(spidev); //則是否spidev_data內(nèi)存 } mutex_unlock(&device_list_lock); return status; } static const struct file_operations spidev_fops = { //文件操作函數(shù)集 .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內(nèi)存 if (!spidev) return -ENOMEM; spidev->spi = spi; //設(shè)置spidev_data->spi(spi設(shè)備) 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); //查找次設(shè)備位圖分配次設(shè)備號 if (minor < N_SPI_MINORS) { struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //計算出設(shè)備號 //創(chuàng)建設(shè)備/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) { //分配設(shè)備號成功 set_bit(minor, minors); //更新次設(shè)備位圖 list_add(&spidev->device_entry, &device_list); //添加進(jìn)設(shè)備鏈表 } 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); //根據(jù)spi設(shè)備獲取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); //清除次設(shè)備位圖對應(yīng)位 if (spidev->users == 0) //使用者個數(shù)為0 kfree(spidev); //釋放spidev_data內(nèi)存 mutex_unlock(&device_list_lock); //解互斥鎖 return 0; } static struct spi_driver spidev_spi_driver = { //spi設(shè)備驅(qū)動 .driver = { .name = "spidev", .owner = THIS_MODULE, }, .probe = spidev_probe, //spidev的probe方法(當(dāng)注冊了modalias域為"spidev"的spi設(shè)備或板級設(shè)備,則會調(diào)用probe方法) .remove = __devexit_p(spidev_remove), //spidev的remove方法 }; static int __init spidev_init(void) //spidev接口初始化 { int status; BUILD_BUG_ON(N_SPI_MINORS > 256); //注冊字符設(shè)備,主設(shè)備號SPIDEV_MAJOR=153,捆綁的設(shè)備操作函數(shù)集為spidev_fops status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); if (status < 0) return status; spidev_class = class_create(THIS_MODULE, "spidev"); //創(chuàng)建設(shè)備類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設(shè)備驅(qū)動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設(shè)備驅(qū)動spidev_spi_driver class_destroy(spidev_class); //注銷設(shè)備類spidev_class unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); //注銷字符設(shè)備 } 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[] = { //要發(fā)送的數(shù)據(jù)數(shù)組 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, }; //接收的數(shù)據(jù)數(shù)據(jù) struct spi_ioc_transfer tr = { //聲明并初始化spi_ioc_transfer結(jié)構(gòu)體 .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的數(shù)量 ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //ioctl默認(rèn)操作,傳輸數(shù)據(jù) if (ret < 1) pabort("can't send spi message"); for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收緩沖區(qū) if (!(ret % 6)) //6個數(shù)據(jù)為一簇打印 puts(""); printf("%.2X ", rx[ret]); } puts(""); } static void print_usage(const char *prog) //參數(shù)錯誤則打印幫助信息 { 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[] = { //參數(shù)命令表 { "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': //設(shè)備名 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': //從機(jī)拉低電平停止數(shù)據(jù)傳輸 mode |= SPI_READY; break; default: //錯誤的參數(shù) print_usage(argv[0]); break; } } } int main(int argc, char *argv[]) { int ret = 0; int fd; parse_opts(argc, argv); //解析傳遞進(jìn)來的參數(shù) fd = open(device, O_RDWR); //打開設(shè)備文件 if (fd < 0) pabort("can't open device"); /* * spi mode //設(shè)置spi設(shè)備模式 */ 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 //設(shè)置每個字含多少位 */ 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 //設(shè)置速率 */ 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); //關(guān)閉設(shè)備 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個數(shù)據(jù)包新聞熱點
疑難解答