內核中的數據與用戶空間數據交互常用的函數有copy_to_user,copy_from_user,和宏定義put_user,get_user,__put_user,__get_user。copy_from_user和copy_to_user函數複製塊數據,如數組,結構體;put_user,get_user,__put_user,__get_user複製的內存是簡單類型,如char,int,long,而且只能複製1,2,4,8個字節。put_user,get_user,__put_user,__get_user執行效率比copy_to_user,copy_from_user的效率要高很多。put_user和__put_user區別在於,前者會調用access_ok進行內核地址的檢查,而後者不進行地址檢查。bq27501驅動的功能是向用戶空間提供電池相關的信息;而且不需要用戶空間向電池輸入數據;另外電池的信息都是很小的數據,都可以使用unsigned short表示,所以沒有必要使用copy_to_user塊數據傳遞的函數;另外再根據《Linux驅動程序》書中“大多數驅動程序代碼中都不需要access_ok,內存管理程序會處理它”,所以選擇__put_user來向用戶空間傳遞電池信息數據。
__put_user(var, ptr),var將內核中的數據var複製到用戶空間;ptr是用戶地址空間的指針,指向內核空間中ioctl最後一個參數。bq27501與用戶空間交互的代碼片段如下: static long bq27501_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { BATT_INFO *pBattInfo = &BattInfo; unsigned short temp; switch(cmd) { case GET_RM: if((i2c_read_reg(&bq27501_i2c_client,RM_REG_ADDR_L, &pBattInfo->RemainingCapacity))>=0){ printk(KERN_ALERT"RaminingCapacity is %u mAh ...\n",pBattInfo->RemainingCapacity); __put_user(pBattInfo->RemainingCapacity, (int __user *)arg); } break; …… } 在用戶空間, ioctl 系統調用的原型爲:int ioctl(int fd, unsigned long cmd, ...); 這個原型中的點表示函數有一個單個可選的參數, 傳統上標識爲 char *argp. 這些點在那裏只是爲了阻止在編譯時的類型檢查。第二個參數,是用戶向驅動傳遞的命令(如讀取剩餘電量值)。第三個參數的實際特點依賴所發出的特定的控制命令,即第二個參數。一些命令不用參數, 一些用一個整數值, 以及一些使用指向其他數據的指針。是否使用參數和指針是根據打開字符設備的方式決定,打開設備的方式有三種,在fcntl.h有其宏定義,只讀,只寫,讀寫,如下所示: #define O_RDONLY 00 #define O_WRONLY 01 #define O_RDWR 02 用戶空間讀取驅動中參數代碼片段如下: if((fd = open("/dev/bq27501",2)) == -1) { perror("device open err!\n"); return -1; } printf("open ok!\n"); ioctl(fd,atoi(argv[1]),&v); //v是一個整型,用於保存內核空間傳遞過來的數據 printf("(2)........cmd = [%d],val = [%d]...........\n",atoi(argv[1]),v);