s1005056 发表于 2014-5-12 15:44:17

無法新增led範例內ioctl的操作功能

各位版友們好,

在寫GPIO練習的時候遇上一些問題,還請高手幫忙看一下。因為想提供多一點細節,文長還請多包涵。
想要達成的目的是GPH3_0輸出高電平,利用GPH3_3監看輸入電平,然後在ioctl內多出IOCTL_GPIO_READ的操作,其他架構跟LED範例手冊內的相同。
程式碼如下:
/* use led sample to test GPH3 is fine under control */
/* run a complete LED sample, just change pins! */

#include <linux/miscdevice.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#include <mach/map.h>
#include <mach/gpio.h>
//#include <mach/gpio-bank.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-cfg-helpers.h>



#define DEVICE_NAME "led3" /* change the name! */

enum{
      IOCTL_GPIO_ON = 0,
      IOCTL_GPIO_READ,
      IOCTL_GPIO_OFF,
};

//#define IOCTL_GPIO_ON      0
//#define IOCTL_GPIO_OFF      1
//#define IOCTL_GPIO_READ      2

static unsigned long gpio_table [] =
{
      S5PV210_GPH3(1), /* output, KP_ROW1, have voltage change */
      S5PV210_GPH3(3), /* input, KP_ROW3 */
};

static unsigned int gpio_cfg_table [] =
{
//      S3C_GPIO_SFN(1),
//      S3C_GPIO_SFN(1),
      S3C_GPIO_SFN(1),
      S3C_GPIO_SFN(0),
};


static int tq210_gpio_open(struct inode *inode, struct file *file)
{
      int i;
      int err;
      err = gpio_request(gpio_table, "GPH3_1");
      if(err)
      {
                printk(KERN_ERR "failed to request GPH3_1 for LVDS PWDN pin\n");
      return err;
      }
      err = gpio_request(gpio_table, "GPH3_3");
      if(err)
      {
                printk(KERN_ERR "failed to request GPH3_3 for LVDS PWDN pin\n");
      return err;
      }
      printk(KERN_INFO " leds opened\n");
      for (i = 0; i < sizeof(gpio_table)/sizeof(unsigned long); i++)
      {
                s3c_gpio_cfgpin(gpio_table, gpio_cfg_table);
                // gpio_direction_output(gpio_table, 0);
      }

      gpio_direction_output(gpio_table, 0);
      gpio_direction_input(gpio_table);

      return 0;

}


static long tq210_gpio_ioctl(
      struct file *file,
      unsigned int cmd,
      unsigned long arg)
{
      int res;
      
      printk(KERN_INFO "+%s, cmd = %hd\n", __func__, cmd);
               
      arg -= 1;
      if (arg > sizeof(gpio_table)/sizeof(unsigned long))
      {
                printk(KERN_INFO "error! ");
                return -EINVAL;
      }

      switch(cmd)
      {
                case IOCTL_GPIO_ON:
                        printk(KERN_INFO " HIGH ");
                        gpio_direction_output(gpio_table, 1);
                        return 0;

                case IOCTL_GPIO_OFF:
                        printk(KERN_INFO " LOW ");
                        gpio_direction_output(gpio_table, 0);
                        return 0;

                case IOCTL_GPIO_READ:
                        res = gpio_get_value(gpio_table);
                        if (res < 0){
                              printk(KERN_INFO "(X) Fail");
                        }else if(res == 0){
                              printk(KERN_INFO " LOW ");
                        }else{
                              printk(KERN_INFO " HIGH");
                        }
                        return 0;

                default:
                        return -EINVAL;
      }
}

static int tq210_gpio_close(struct inode *inode, struct file *file)
{
      gpio_free(gpio_table);
      gpio_free(gpio_table);
      printk(KERN_INFO "TQ210 LEDs driver successfully close\n");
      return 0;
}

static struct file_operations dev_fops = {
      .owner      =      THIS_MODULE,
      .unlocked_ioctl      =      tq210_gpio_ioctl,
      .open = tq210_gpio_open,
      .release = tq210_gpio_close,
};

static struct miscdevice misc = {
      .minor = MISC_DYNAMIC_MINOR,
      .name = DEVICE_NAME,
      .fops = &dev_fops,
};

static int __init dev_init(void)
{
      int ret;

      ret = misc_register(&misc);
      if(ret == 0)
                printk(KERN_INFO "TQ210 LEDs driver successfully probed\n");

      return ret;
}

static void __exit dev_exit(void)
{
      misc_deregister(&misc);
      gpio_free(gpio_table);
      gpio_free(gpio_table);
      printk(KERN_INFO "TQ210 LEDs driver successfully exit\n");
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Paul <shufexiu@163.com>");
MODULE_DESCRIPTION("LEDS' Driver");

測試用之應用程式的主要流程:open->LED1輸出高電平->讀取LED2腳位10次->LED2輸出低電平,期間可以手動插拔電線就可以改變電平,確認輸入值的變動。
程式碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <asm/types.h>
#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <fcntl.h>
#include<string.h>
#include<unistd.h>
#include<sys/mman.h>
#include <sys/poll.h>

#define DEV_FILE_NAME                "/dev/led3"      
/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */   
/* #define IOCTL_GPIO_ON      0
#define IOCTL_GPIO_OFF      1
#define IOCTL_GPIO_READ      2 */
enum{
      IOCTL_GPIO_ON = 0,
      IOCTL_GPIO_READ,
      IOCTL_GPIO_OFF,
};

#define LED1                1               
#define LED2                2               
                     
int main(void)
{
      int devfd;
      int i,err;
      
      devfd = open(DEV_FILE_NAME,O_RDWR);               

      if(devfd < 0)                        
      {
                printf("can't open dev (%s)",DEV_FILE_NAME);
                return 0;
      }


      err = ioctl(devfd,IOCTL_GPIO_ON,LED1);
      if(err<0)
                printf("GPIO_ON faild! (%d)\n",err);
      sleep(1);      
      
      for(i=0;i<10;++i)
      {
               
                err = ioctl(devfd,IOCTL_GPIO_READ,LED2);
                if(err<0)
                        printf("GPIO_READ faild! (%d)\n",err);                        
                sleep(2);      

      }

      err = ioctl(devfd,IOCTL_GPIO_OFF,LED1);//关闭LED1。
      if(err<0)
                printf("GPIO_OFF faild! (%d)\n",err);
      sleep(2);      //休眠1S
      
      close(devfd);//关闭设备。对应驱动中的tq210_gpio_close
      return 0;
}


目前遇到的問題是,在最前面定義ioctl的cmd參數時,不曉得為什麼只能新增兩個參數,不管是用define或是enum,不管是從0開始還是從1開始,都會發生如下的狀況:
(一)參數從0開始
enum{
      IOCTL_GPIO_ON = 0,
      IOCTL_GPIO_READ,
      IOCTL_GPIO_OFF,
};則輸出如下圖,可以發現IOCTL_GPIO_ON(0)以及IOCTL_GPIO_READ(1)皆正常進入操作,而IOCTL_GPIO_OFF(2)則運行失敗...



(二)後來想把enum從1開始試試,
enum{
IOCTL_GPIO_ON = 1,
IOCTL_GPIO_READ,
IOCTL_GPIO_OFF,
};卻是IOCTL_GPIO_ON(1)以及IOCTL_GPIO_OFF(3)正常運作,反而是夾在中間的IOCTL_GPIO_READ(2)無法進行操作


到底要怎麼修改才能加入新的操作呢...或是有哪裏使用了不正確的語法,還請各位大師們解惑,感激不盡~~

Calvin 发表于 2014-5-13 13:42:12

本帖最后由 Calvin 于 2014-5-13 13:43 编辑

好低级的错误啊{:5_301:}自己围绕一下数组的相关知识再查看一下 最好再仔细看一下我们提供的例程;你自己的程序有问题

s1005056 发表于 2014-5-13 16:21:26

Calvin 发表于 2014-5-13 13:42
好低级的错误啊自己围绕一下数组的相关知识再查看一下 最好再仔细看一下我们提供的例程;你自己 ...

正是因為看了一兩天還看不出來所以才上來詢問的~"~
做練習的意義就是想要知道自己哪裡還沒看懂,但是現在這個錯誤實在毫無頭緒...

可以請Calvin再多給些提示嗎?

Calvin 发表于 2014-5-14 08:59:44

s1005056 发表于 2014-5-13 16:21
正是因為看了一兩天還看不出來所以才上來詢問的~"~
做練習的意義就是想要知道自己哪裡還沒看懂,但是現 ...

你一行一行对比我们给的例程 就可以发现了

s1005056 发表于 2014-5-14 11:56:00

本帖最后由 s1005056 于 2014-5-14 13:19 编辑

------如下------

s1005056 发表于 2014-5-14 11:57:30

剛剛在比對的過程中,想到會不會是ioctl的cmd是不正確的,亦即不能使用2作為cmd,因為兩個方式都是無法讓參數2的命令正確被執行,似乎在user space就被擋下了

s1005056 发表于 2014-5-14 13:19:10

Calvin 发表于 2014-5-14 08:59
你一行一行对比我们给的例程 就可以发现了

剛剛比對了一下,還是沒意會到關於数组的任何錯誤{:5_303:}

可以請Calvin大指出是哪一個函數的哪部分嗎?
或許是我的觀念原本就錯了,所以怎麼看也看不出來{:2_136:}
感謝您的幫忙

s1005056 发表于 2014-5-14 13:20:24

Calvin 发表于 2014-5-14 08:59
你一行一行对比我们给的例程 就可以发现了

剛剛在比對的過程中,想到會不會是ioctl的cmd是不正確的,亦即不能使用2作為cmd,因為兩個方式都是無法讓參數2的命令正確被執行,似乎在user space就被擋下了

s1005056 发表于 2014-5-14 13:40:46

本帖最后由 s1005056 于 2014-5-14 14:40 编辑

由於在比對之後還是沒看到有什麼錯誤,所以還是把我的想法先列出來,Calvin大可以直接點出在哪一個步驟有問題。我想這樣會比較快,若是一開始就想錯了,再看個幾百次也沒用。

首先是驅動程式:

變數新增的部分:
1. 不想與TQ210自帶的led驅動相重疊,所以修改了一個DEVICE_NAME成為"led3"
2. 需要多一個IOCTL_GPIO_READ指令,使用enum列舉需要的狀態,以後需要新增刪除指令時也不會太麻煩
3. 修改gpio_table,改為使用GPH3_1以及GPH3_3的腳位
4. 修改gpio_cfg_table,需要GPH3_1為輸出,而GPH3_3為輸入,參考s5pv210晶片手冊,確認S3C_GPIO_SFN之參數設定,搜尋kernel源碼,確認該管腳沒有被占用(key_ownx.c為自行新增之檔案)















dev_init以及dev_exit:
1. 在init的部分,將CONFIG_TQ210_DEBUG_LEDS的部分刪去,將這部分的功能留在open的時候才執行。因此init內部僅剩註冊功能,加入ret==0判斷是確保device driver有正常註冊
2.在exit的部分,維持原有的撤銷driver以及釋放gpio
3. miscdevice以及file_operations結構皆與範例程式相同

tq210_gpio_open與tq210_gpio_close中對管腳的設定:
1. 修改gpio_request申請的gpio管腳以及label
2. 修改for迴圈中對管腳的初始設定,官方範使用s3c_gpio_cfgpin(gpio_table, gpio_cfg_table)為管腳配置指定功能,這部分繼續放置在for迴圈中;而gpio_direction_output(gpio_table, 0);為方向標記設定,由於管腳需分別進行輸出/輸入方向標記設定,因此拉出for迴圈之外
3. gpio_direction_output設定為模仿範例設定;gpio_direction_input設定為參考說明手冊,在http://www.eoeandroid.com/blog-23065-4660.html中說明gpio_direction_input似乎僅設定訊號方向,而非如手冊中所述可以「讀取管腳gpio狀態」,這部分還待版上高手分析與解惑,目前功力還無法參透源碼中的含意
4. tq210_gpio_close與範例程式相同



tq210_gpio_ioctl對指令的設定:
1. 使用printk顯示當前收到的cmd數值,另一方面也是可以確認驅動程式是否有正確執行,若有成功呼叫到驅動,則會顯示該行訊息,然而在使用測試程式的ioctl呼叫錯誤發生時,該行訊息並無出現,故推測應該是卡在更之前的流程
2. 在switch中新增「case IOCTL_GPIO_READ:」, 使用http://www.eoeandroid.com/blog-23065-4660.html中所述之gpio_get_value函數來實作讀取管腳輸入數值的動作,並依據回傳值而有不同的printk輸出





s1005056 发表于 2014-5-14 15:22:19

接下來為應用測試程式:

變數設置:
1. 修改DEV_FILE_NAME為"/dev/led3"與驅動程式內DEVICE_NAME相符
2. 設置IOCTL_GPIO_READ指令,一樣使用enum列舉需要的指令
3. 續用LED1與LED2做識別




open device file與範例相同




接下來的部分更動了程式流程:
1. 讓GPH3_1持續輸出高電平
2. 之後便每隔兩秒讀取GPH3_3的輸入值,讀取10次
3. 之後便關閉GPH3_3的輸出,關閉device file


1 & 3皆與範例程式相同





2的部分則為自行新增的功能
err = ioctl(devfd,IOCTL_GPIO_READ,LED2);,關於第2個參數IOCTL_GPIO_READ,應用程式與驅動使用相同的enum進行枚舉,故應為相同之數值2;關於第3個參數,在應用程式中雖然為2,但是tq210_gpio_ioctl驅動中有將arg -=1;因此是指向gpio_table沒錯,(Calvin大所說與数组有關的錯誤,目前只想到可能會是這裡,不知道哪裡還有錯誤的用法,還請Calvin大提醒)





s1005056 发表于 2014-5-15 13:41:59

本帖最后由 s1005056 于 2014-5-20 17:32 编辑

--如#9所示--

s1005056 发表于 2014-5-16 17:36:51

本帖最后由 s1005056 于 2014-5-20 17:26 编辑

如#10所示

s1005056 发表于 2014-5-20 17:30:36

Calvin 发表于 2014-5-14 08:59
你一行一行对比我们给的例程 就可以发现了

Calvin版主好:
這幾天還是看不出哪裡有數組的相關錯誤,已將程式碼以及流程置於9樓、10樓,還請您抽空解惑一下,謝謝。

s1005056 发表于 2014-5-23 16:44:20

Calvin 发表于 2014-5-13 13:42
好低级的错误啊自己围绕一下数组的相关知识再查看一下 最好再仔细看一下我们提供的例程;你自己 ...

Calvin版主好:
這幾天還是看不出哪裡有數組的相關錯誤,已將程式碼以及流程置於9樓、10樓,還請您抽空解惑一下,謝謝。

copyleft 发表于 2014-6-3 10:18:42

这个问题我也出现过,在ioctl中我试了很久才发现:
    在内核驱动中ioctl中cmd参数如果是2的话,好像无论如何都不能执行成功,解决办法:
#define IOCTL_GPIO_OFF      0
#define IOCTL_GPIO_ON      1
#define IOCTL_GPIO_READ    3
也就是 ioctl 的命令直接跳过2,就可以了,具体原因我也不知道为什么,感觉好像在2在什么地方已经使用了,但到底在什么使用了,这我就不知道了,我从Android上面传入内核中ioctl的cmd为2时,在内核通过printf得到的cmd好像变成了0,好像是这样,我记不准确了,你可以试试在内核中输出cmd,一个比较方便的解决 方法 就是直接跳过2,使用后面的数字作为cmd
页: [1] 2
查看完整版本: 無法新增led範例內ioctl的操作功能