嵌入式Linux驅動——SPI子系統解讀(四)
阿新 • • 發佈:2019-02-09
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; //對使用者空間的指標進行檢查,分成讀寫兩部分檢查,IOC_DIR來自於使用者,access_ok來自核心 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; //將檔案的私有資料給spidev spin_lock_irq(&spidev->spi_lock); spi = spi_dev_get(spidev->spi); //獲取spi_device 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(temp & ~SPI_MODE_MASK) { retval == 0; break; //模式錯誤,則跳出switch } tmp |= spi->mode & ~SPI_MODE_MASK; spi->mode = (u8)tmp; retval = spi_setup(spi); //先呼叫spi_setup函式,然後呼叫s3c64xx_spi_setup 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) //引數為正數,設定為LSB spi->mode |= SPI_LSB_FIRST; else //引數為0,則設定為非LSB spi->mode &= ~SPI_LSB_FIRST; retval = spi_setup(spi); //先呼叫spi_setup函式,然後呼叫s3c64xx_spi_setup if(retval < 0) spi->mode = save; //呼叫不成功則恢復引數 else dev_dbg(&spi->dev,"%csb first\n",tmp?'l':'m'); } 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); //先呼叫spi_setup函式,然後呼叫s3c64xx_spi_setup if(retval < 0) spi->max_speed_hz = save; else dev_dbg(&spi->dev,"%d Hz (max)\n",tmp); } break; default: //分段或者全雙工IO要求(全雙工,接收發送資料) if(_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) || _IOC_DIR(cmd) != _IOC_WRITE) { retval = -ENOTTY; break; } tmp = _IOC_SIZE(cmd); //獲取引數的大小,引數為spi_ioc_transfer if((tmp % sizeof(struct spi_ioc_transfer)) != 0) { //檢查tmp是否為spi_ioc_transfer的整數倍 retval = -EINVAL; break; } n_ioc = tmp / sizeof(struct spi_ioc_transfer); if(n_ioc == 0) //計算傳進來的資料共有幾個spi_ioc_transfer break; ioc = kmalloc(tmp,GFP_KERNEL); if(!ioc) { retval = -ENOMEM; break; } //從使用者空間拷貝spi_ioc_transfer陣列,不對使用者空間指標進行檢查 if(__copy_from_user(ioc,(void __user *)arg,tmp)) { kfree(ioc); retval = -EFAULT; break; } retval = spidev_message(spidev,ioc,n_ioc); //傳遞給spi_message函式執行 kfree(ioc); break; } mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); //減少引用計數 return retval; }