1. 程式人生 > >linux驅動學習4:scull驅動

linux驅動學習4:scull驅動

要點:學習ioctl()驅動編寫,如何傳入命令來控制硬體。

1ioctl知識

使用者空間的ioctl()呼叫如下:

int  ioctl(int fd,  unsigned long  cmd, …);

2)驅動中ioctl:

Int  (*ioctl) (struct inode  *inode,  struct file  *filp,  unsigned int  cmd,  unsigned long  arg );

主要工作:傳入裝置對應的inode,根據命令cmdarg來修改filp

3)實現

分為兩步:

n  定義命令(一般在標頭檔案中, scull.h);

n  實現命令, switch

i.              定義命令

編寫ioctl之前先需要定義命令,命令號在系統範圍內必須是唯一的。命令cmd被劃分為4個位段:型別type(幻數:確保唯一)、序號(對應該裝置驅動的命令的序號)、傳送方向、引數大小。

type幻數(型別):表明哪個裝置的命令,在參考了ioctl-number.txt之後選出,8位寬

number序號,表明裝置命令中的第幾個,8位寬

direction資料傳送的方向,可能的值是_IOC_NONE(沒有資料傳輸),_IOC_READ_IOC_WRITE。資料傳送是從應用程式的觀點來看的,_IOC_READ意思是從裝置讀

size使用者資料的大小。(13/14

位寬,視處理器而定)

核心提供了下列巨集來幫助定義命令:

_IO(type, nr):沒有引數傳遞的命令。(那麼direction的值為_IOC_NONEsize的值為0

_IOR(type, nr, datatype):從驅動中讀資料(4個值已經確定)

_IOW(type, nr, datatype):寫資料到驅動

_IOWR(type, nr, datatype):雙向傳送,typenumber成員作為引數被傳遞

定義命令(範例)

#define   MEM_IOC_MAGIC  'm' //定義幻數,一個字母剛好是8位,必須唯一

#define          MEM_IOCSET       _IOW(MEM_IOC_MAGIC, 0, int)

#define       MEM_IOCGQSET     _IOR(MEM_IOC_MAGIC, 1, int)

ii.              實現

1)  返回值:switch

2)  傳入引數:如果是整數,則可直接使用;如果是指標,則需要先檢驗該指標是否有效(用access_ok()檢驗該指標是否是使用者空間合法的)。

注:

不需要檢測的函式:copy_to_user(),  copy_from_user(),  get_user(),  put_user();

需要用access_ok檢驗的:__get_user()核心從使用者空間讀資料,   __put_user()核心向用戶空間寫資料.(此處要注意access_ok() _IOC_READ的反向問題)即如下語句:

if (_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

_IOC_READ:表示使用者讀取核心裝置的資料(主體是使用者程式);

access_ok()write是驗證核心可以向用戶空間的指標寫資料(主體是核心)。

4int  access_ok(int type,  const void* addr,  unsigned long size)

第一個引數是VERIFY_READ或者VERIFY_WRITE,用來表明是讀使用者記憶體還是寫使用者記憶體。Addr引數是要操作的使用者記憶體地址,size是操作的長度。如果ioctl需要從使用者空間讀一個整數,那麼size引數等於sizeof(int)access_ok返回一個布林值:1是成功(存取沒問題)和0是失敗(存取有問題),如果該函式返回失敗,則ioctl應當返回-EFAULT.

5ioctl程式碼:先檢驗

int scull_ioctl(struct inode *inode,  struct file *filp,  unsigned int cmd,  unsigned long arg)
{
	int err = 0, tmp;
	int retval = 0;
	/*
	 * extract the type and number bitfields, and don't decode
	 * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
	 */
	if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
	if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;

	if (_IOC_DIR(cmd) & _IOC_READ)
		err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
	if (err) return -EFAULT;

	switch(cmd) {
	  case SCULL_IOCRESET:
		scull_quantum = SCULL_QUANTUM;
		scull_qset = SCULL_QSET;
		break;
        
	  case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		retval = __get_user(scull_quantum, (int __user *)arg);
		break;

	  case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
		if (! capable (CAP_SYS_ADMIN))
			return -EPERM;
		scull_quantum = arg;
		break;

	  case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
		retval = __put_user(scull_quantum, (int __user *)arg);
		break;
  	  default:  /* redundant, as cmd was checked against MAXNR */
		return -ENOTTY;
	}
	return retval;
}