国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

linux spi驅(qū)動開發(fā)學(xué)習(xí)-----spidev.c和spi test app

2019-11-09 14:29:53
字體:
供稿:網(wǎng)友

 轉(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ù)包 
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 新泰市| 斗六市| 汾西县| 肇州县| 合肥市| 常熟市| 琼结县| 瓮安县| 文安县| 乾安县| 壶关县| 鹿泉市| 巩留县| 县级市| 平塘县| 沁源县| 合肥市| 佛山市| 五家渠市| 中方县| 化隆| 六枝特区| 南开区| 六安市| 莒南县| 汉寿县| 中卫市| 南和县| 读书| 宣武区| 怀安县| 台东县| 岚皋县| 西宁市| 天门市| 临海市| 屏山县| 宁都县| 巴中市| 贡觉县| 张家川|