移植mini2440 2.6.32.2內核LED驅動到3.10.17

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>


#define DEVICE_NAME "leds" //設備名(/dev/leds)
//LED 對應的GPIO 端口列表
static unsigned long led_table[] = 
{
S3C2410_GPB(5), 
S3C2410_GPB(6), 
S3C2410_GPB(7), 
S3C2410_GPB(8), 
};
//LED 對應端口將要輸出的狀態列表
static unsigned int led_cfg_table[] = {


S3C2410_GPIO_OUTPUT, 
S3C2410_GPIO_OUTPUT, 
S3C2410_GPIO_OUTPUT, 
S3C2410_GPIO_OUTPUT, 
};

/*ioctl 函數的實現
 * 在應用/用戶層將通過ioctl 函數向內核傳遞參數,以控制LED 的輸出狀態
 */
static int sbc2440_leds_ioctl(
struct inode *inode, 
struct file *file, 
unsigned int cmd, 
unsigned long arg)
{


switch(cmd) {


case 0:
case 1:
if (arg > 4) 
{


return -EINVAL;
}
//根據應用/用戶層傳遞來的參數(取反),通過s3c2410_gpio_setpin
//             函數設置LED 對應的端口寄存器,
//s3c_gpio_setpin(led_table[arg], !cmd);
gpio_set_value(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
/*
 * 設備函數操作集,在此只有ioctl 函數,通常還有read,  write,
 open,  close 等,因爲本LED 驅動在下面已經
 * 註冊爲misc 設備,因此也可以不用open/close
 */
static struct file_operations dev_fops = {


.owner = THIS_MODULE, 
.ioctl = sbc2440_leds_ioctl, 
};
/*
 * 把LED 驅動註冊爲MISC 設備
 */
static struct miscdevice misc = {


.minor = MISC_DYNAMIC_MINOR,  //動態設備號
.name = DEVICE_NAME, 
.fops = &dev_fops, 
};
/*
 * 設備初始化
 */
static int __init dev_init(void)
{


int ret;
int i;
for (i = 0; i < 4; i++) {


//設置LED 對應的端口寄存器爲輸出(OUTPUT)
s3c2410_gpio_cfgpin(led_table[i],  led_cfg_table[i]);
//設置LED
//              對應的端口寄存器爲低電平輸出,在模塊加載結束後,四個LED
//              應該是全部都是發光
//                      //狀態
s3c2410_gpio_setpin(led_table[i],  0);
}
ret = misc_register(&misc); //註冊設備
printk (DEVICE_NAME"\tinitialized\n");
//打印初始化信息
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
//模塊初始化,僅當使用insmod/podprobe
//命令加載時有用,如果設備不是通過模塊方式加載,此處將不會被調用
module_exit(dev_exit);//卸載模塊,當該設備通過模塊方式加載後,可以通過rmmod
    // 命令卸載,將調用此函數
MODULE_LICENSE("GPL"); //版權信息
MODULE_AUTHOR("RenZhe gzs"); //開發者信息


Makefile文件:


KDIR = /work/src/linux-2.6.32.2/
appgcc = /work/toolschain/arm-2013.05/bin/arm-none-linux-gnueabi-gcc
obj-m := led.o

all:
make -C $(KDIR) M=$(PWD)
sudo cp led.ko /work/nfs/root/root#編譯後複製到nfs

clean:
make -C $(KDIR) M=$(PWD) clean

源碼已經在2.6內核編譯通過,修改Makefile文件後編譯

make -C /work/src/linux-3.10.17/ M=/work/drivers/arm_led
make[1]: 正在進入目錄 `/work/src/linux-3.10.17'
  LD      /work/drivers/arm_led/built-in.o
  CC [M]  /work/drivers/arm_led/led.o
/work/drivers/arm_led/led.c:80: error: unknown field 'ioctl' specified in initializer
/work/drivers/arm_led/led.c:80: warning: initialization from incompatible pointer type
/work/drivers/arm_led/led.c: In function 'dev_init':
/work/drivers/arm_led/led.c:102: error: implicit declaration of function 's3c2410_gpio_cfgpin'
/work/drivers/arm_led/led.c:107: error: implicit declaration of function 's3c2410_gpio_setpin'
make[2]: *** [/work/drivers/arm_led/led.o] 錯誤 1
make[1]: *** [_module_/work/drivers/arm_led] 錯誤 2
make[1]:正在離開目錄 `/work/src/linux-3.10.17'
make: *** [all] 錯誤 2


原因是:在3.10.17內核上file_operations發生了重大的改變:

原先的

  int (*ioctl)(struct inode*, struct file*, unsigned int, unsigned long);

被改爲了       

   long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

因而在實際驅動中,我們需要將原先的寫的ioctl函數頭給改成下面的unlocked_ioctl,在file_operations結構體的填充中也是一樣。

error: unknown field 'ioctl' specified in initializer問題是由於3.10.17內核之後 去掉了原來的ioctl,添加兩個新的成員,所以會出錯

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

 所以修改源文件中file_operations內.ioctl 改爲 .compat_ioctl 即可


至於警告是因爲參數減少了一個,去掉即可


用cgvg查找3.10內核目錄發現沒有(設置IO口爲輸出)s3c2410_gpio_cfgpin這個函數,對照/drivers/leds/leds-s3c24xx.c這個驅動文件發現設置輸出的函數改爲了gpio_direction_output函數原型爲gpio_direction_output(unisgned gpio, int value);第一個參數爲GPIO,第二個爲新gpio_set_value(port_num,0/1)gpio_set_value(port_num,0/1)


s3c2410_gpio_setpin對應函數gpio_set_value(port_num,0/1)設置新狀態


以下是轉載內容:

在kernel 2.6.36 中已經完全刪除了struct file_operations 中的ioctl 函數指針,取而代之的是unlocked_ioctl 。

這個指針函數變了之後最大的影響是參數中少了inode , 不過這個不是問題,因爲用戶程序中的ioctl對應的系統調用接口沒有變化,所以用戶程序不需要改變,一切都交給內核處理了,如果想在unlocked_ioctl中獲得inode 等信息可以用如下方法:
struct inode *inode = file->f_mapping->host;
struct block_device *bdev = inode->i_bdev;
struct gendisk *disk = bdev->bd_disk;
fmode_t mode = file->f_mode;
struct backing_dev_info *bdi;

這次內核函數的變化引出了一個問題,從ioctl系統調用往後,真正的ioctl調用順序是什麼?爲什麼compat_ioctl 不被調用?
compat_ioctl被使用在用戶空間爲32位模式,而內核運行在64位模式時。這時候,需要將64位轉成32位。
以下是2.6.36的情況:
SYSCALL_DEFINE3(ioctl ...) compat_sys_ioctl (是否直接調用compat_ioctl 取決於compat_ioctl 是否存在)
| | |-----> compat_ioctl
|   |
|------>do_vfs_ioctl (下一步的調用取決於file->f_path.dentry->d_inode->i_node)
|            |------>file_ioctl
| |
|-------------------------------->vfs_ioctl
|------->unlock_ioctl
其實compat_ioctl 沒有被調用的原因是compat_sys_ioctl 沒有被調用,而它沒有被調用的原因似乎是壓根就沒有編譯到內核中,因爲我沒有找到調用這個函數的代碼。
unlocked_ioctl 實際上取代了用了很久的ioctl,主要的改進就是不再需要上大內核鎖 (調用之前不再先調用lock_kernel()然後再unlock_kernel())
總的來說kernel 開發者正在試圖朝移除大內核鎖的方向努力,ioctl的移除就是被革命了。相信以後越來越多的內核函數會擺脫大內核鎖的依賴,並且大內核鎖最終會被移除。

(以上內容爲轉載)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章