4412 GPIO初始化

1、GPIO的初始化

在內核源碼目錄下使用命令ls drivers/gpio/*.o,能夠看到gpioexynos4被編譯進了內核.經過搜索*.o文件,能夠知道內核編譯內哪些文件。針對的看能夠簡化不少。
生成.o文件表明最終被編譯進了內核
除了menuconfig配置文件,還能夠經過.o文件來斷定該文件是否編譯進了
node

ls drivers/gpio/*.o

 

內核

gpio-exynos4.c文件最下面一行
core_initcall(exynos4_gpiolib_init);
core_initcall表明在linux初始化過程當中會調用
初始化函數是在源碼目錄下include/linux/init.h文件中定義的,該頭文件中定義了一系列的初始化函數,在linux啓動的過程當中會按等級
linux

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 */
#define pure_initcall(fn)               __define_initcall("0",fn,0)

#define core_initcall(fn)               __define_initcall("1",fn,1)
#define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
#define postcore_initcall(fn)           __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn)      __define_initcall("2s",fn,2s)
#define arch_initcall(fn)               __define_initcall("3",fn,3)
#define arch_initcall_sync(fn)          __define_initcall("3s",fn,3s)
#define subsys_initcall(fn)             __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn)        __define_initcall("4s",fn,4s)
#define fs_initcall(fn)                 __define_initcall("5",fn,5)
#define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn)             __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn)             __define_initcall("6",fn,6)
init.h文件相關部分

初始化函數調用了exynos4_gpiolib_init
經過軟件source insight查找到exynos4_gpiolib_init函數的定義
在該函數中引用了chip = exynos4_gpio_common_4bit結構體
查找到結構體exynos4_gpio_common_4bit
能夠看到結構體中有S5P_VA_XXXX的基地址定義,VA通常用來表明虛擬地址
ios

以有帶有label= "GPL2"的結構體爲例
程序員

 

 

結構體exynos4_gpio_common_4bit
shell

.base = (S5P_VA_GPIO2 + 0x100)
表示偏移地址和虛擬地址相加
.eint_offset = 0x20
表示中斷部分,介紹中斷的時候再講(IO口能夠配置爲中斷模式)
.group = 22
GPIO分組
chip.base = EXYNOS4_GPL2(0),
宏定義EXYNOS4_GPL2(0)賦值給初始化函數
chip.ngpio = EXYNOS4_GPIO_L2_NR
表示這一小組中有幾個GPIO
chip.label = "GPL2",
程序員須要關心的標誌
數組

宏定義EXYNOS4_GPL2(0)分析
EXYNOS4_GPL2(_nr) (EXYNOS4_GPIO_L2_START + (_nr))
枚舉GPIO
EXYNOS4_GPIO_L2_START= EXYNOS4_GPIO_NEXT(EXYNOS4_GPIO_L1)
EXYNOS4_GPIO_NEXT宏定義
#define EXYNOS4_GPIO_NEXT(__gpio) \ ((__gpio##_START) + (__gpio##_NR)
+ CONFIG_S3C_GPIO_SPACE + 1)
GPIO的數量EXYNOS4_GPIO_L2_NR
能夠經過手冊查到
S5P_VA_GPIO2
網絡

虛擬地址

查找S5P_VA_GPIO2宏定義,能夠看到全部的GPIO被分爲4bank,這
個和datasheet上面是一致的。
S5P_VA_GPIO1
S5P_VA_GPIO2 S3C_ADDR(0x02240000)
S5P_VA_GPIO3
S5P_VA_GPIO4
查找到S3C_ADDR宏定義
#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
查找到S3C_ADDR_BASE宏定義,這是一個虛擬地址,能夠看出,地址
範圍超出了1G或者2G內存的範圍
#define S3C_ADDR_BASE 0xF6000000
app

虛擬地址和物理地址映射

虛擬地址通常很好查找,通常在平臺相關gpio的文件中就能夠找到宏定義
source insight中搜索關鍵字S5P_VA_GPIO2,看看那裏用到了這個宏定義。搜索時間會比較長,1-5分鐘吧。
搜索出來以後,能夠看到除了gpio-exynos4.c文件中使用,cpu-exynos中也使用了,這是一個平臺文件
ide

 

結構體解釋
.virtual = (unsigned long)S5P_VA_GPIO2,表示虛擬地址
函數

.pfn = __phys_to_pfn(EXYNOS4_PA_GPIO2),表示物理地址

.length = SZ_4K,表示映射的寬度

.type = MT_DEVICE,

查找到宏定義EXYNOS4_PA_GPIO2

#define EXYNOS4_PA_GPIO2  0x11000000

這個物理地址0x11000000就是

 

初始化過程簡單描述
平臺文件分別定義好物理地址和虛擬地址
物理地址和虛擬地址之間映射
在初始化中,引入了程序員須要使用的GPIO宏定義,並將宏定義裝入chip結構體中

 

GPIO的調用函數

例如頭文件gpio-cfg.hs3c_gpio_cfgpin函數。這個函數是給GPIO作配置,第一個參數是宏EXYNOS4_GPL2(0),第二個是配置的狀態參數
配置頭文件在arm/arm/plat-samsung/include/plat/gpio-cfg.h
查找該函數,能夠看到進入函數就會調用chip結構體
s3c_gpiolib_getchip,這個函數經過pin調用以後,會返回s3c_gpios[chip] 的參數
exynos4_gpio_common_4bit[]s3c_gpios都是結構體s3c_gpio_chip類型的數據
而後計算偏移地址等等一系列操做,這一部分是linux內核以及三星平臺完成的,具體細節不用管。

也就是咱們控制GPIO的時候,能夠經過GPIO的一些處理函數加上相似EXYNOS4_GPL2(0)的宏定義,就能夠操做GPIO
後面再具體介紹GPIO操做中,經常使用函數的使用

不是說好的分頁大小要同樣,怎麼GPIO通過mmu處理的時候,又有SZ_256又有SZ_4K
實際上CPU查找地址的時候,仍舊是經過內存。mmu自己不保存具體的數據,主要是提供一個虛擬地址和物理地址的表格,表格中還有字段的長度。這個分頁和mmu沒什麼關係,是CPU內存以及物理地址之間通訊使用
的概念。這個只是一個抽象的概念,理解mmu只是一個表格,CPU對GPIO的操做就很好理解了。

常見問題

內部寄存器不是很快麼,CPU爲何不直接讀取?
內部寄存器是很快,可是相對於CPU仍是很是慢。CPU處理數據是將內存中一大段一大段處理,若是單個的讀取內部寄存器的值,對CPU是極大的浪費。把內部寄存器也當作特殊的物理地址便可。
只講了虛擬地址和物理地址對應數組,怎麼沒介紹哪裏調用了?
你們能夠看一下函數ioremaplinux會調用這個函數來實現gpio的映射關係
今天講的已經夠多夠深刻了,你們只要可以理解這麼一層意思就能夠了,這個東西對咱們實際寫驅動的幫助其實不是那麼大!
若是我仍是理解不了對宏定義EXYNOS4_GPL2(0)的操做就是對4412芯片管腳AC21寄存器的操做,怎麼辦?
記住這個結論,可以將宏變量EXYNOS4_GPL2(0)GPL這一組GPIO的第0位寄存器聯想起來。
後面跟着我依葫蘆畫瓢,不影響你們實際寫程序,有興趣再回過頭理解

2、LED驅動

原理簡單介紹

三極管(NPN鍺管)
電流控制電流源
三極端CE間的電阻可變,能夠把Rce當作一個可調電阻,可調電阻的變量是電源
IO管腳拉高以後,BE之間達到必定電流,可變電阻Rce就從無限大下降到大概幾百歐姆。
高電平燈亮,低電平燈滅

頭文件

• Linux中申請GPIO的頭文件
– include/linux/gpio.h
• 三星平臺的GPIO配置函數頭文件

– arch/arm/plat-samsung/include/plat/gpio-cfg.h
– 包括三星全部處理器的配置函數
• 三星平臺EXYNOS系列平臺,GPIO配置參數宏定義頭文件
– arch/arm/mach-exynos/include/mach/gpio.h
– GPIO管腳拉高拉低配置參數等等
– 配置參數的宏定義應該在arch/arm/plat-samsung/include/plat/gpio-cfg.h文件中
• 三星平臺4412平臺,GPIO宏定義頭文件。已經包含在頭文件gpio.h中
– arch/arm/mach-exynos/include/mach/gpio-exynos4.h
– 包括4412處理器全部的GPIO的宏定義

#include <linux/module.h>
#include <linux/init.h>

/* device register header file, include device and driver struct
 * register and remove function */
#include <linux/platform_device.h>
/* register misc device header file */
#include <linux/miscdevice.h>
/* register deivce node file operations struct */
#include <linux/fs.h>

/* linux gpio header file */
#include <linux/gpio.h>
/* samsung gpio config file */
#include <plat/gpio-cfg.h>
/* exynos gpio config header file */
#include <mach/gpio.h>
/* 4412 gpio header file */
#include <mach/gpio-exynos4.h>

#define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_ctl"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");

static int hello_open(struct inode *inode, struct file *file)
{
        printk(KERN_EMERG "hello open\n");
        return 0;
}

static int hello_release(struct inode *inode, struct file *file)
{
        printk(KERN_EMERG "hello release\n");
        return 0;
}

static long hello_ioctl(struct file *file, unsigned int cmd ,unsigned long arg)
{
        printk(KERN_EMERG "cmd is %u, arg is %lu\n", cmd, arg);

        if(cmd > 1 || arg > 1) {
                printk(KERN_EMERG "cmd and arg is 0 or 1\n");
                return 0;
        }

        gpio_set_value(EXYNOS4_GPL2(0), cmd);

        return 0;
}

static struct file_operations  hello_ops = {
        .owner   = THIS_MODULE,
        .open    = hello_open,
        .release = hello_release,
        .unlocked_ioctl = hello_ioctl,
};

static struct miscdevice hello_dev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name  = DEVICE_NAME,
        .fops  = &hello_ops,
};

static int hello_probe(struct platform_device *pdv)
{
        int ret;
        printk(KERN_EMERG "\tinitialized\n");

        /* set up gpio */
        ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");
        if(ret < 0) {
                printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed\n");
                return ret;
        }

        s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);

        gpio_set_value(EXYNOS4_GPL2(0), 0);

        /* register */
        misc_register(&hello_dev);

        return 0;
}

static int hello_remove(struct platform_device *pdv)
{
        printk(KERN_EMERG "\tremove\n");
        misc_deregister(&hello_dev);
        return 0;
}

static void hello_shutdown(struct platform_device *pdv)
{

}

static int hello_suspend(struct platform_device *pdv, pm_message_t state)
{
        return 0;
}

static int hello_resume(struct platform_device *pdv)
{
        return 0;
}

struct platform_driver hello_driver = {
        .probe    = hello_probe,
        .remove   = hello_remove,
        .shutdown = hello_shutdown,
        .suspend  = hello_suspend,
        .resume   = hello_resume,
        .driver = {
                .name  = DRIVER_NAME,
                .owner = THIS_MODULE,
        }
};

static int hello_init(void)
{
        int DriverState;

    printk(KERN_EMERG "Hello world enter!\n");
        DriverState = platform_driver_register(&hello_driver);

        printk(KERN_EMERG "\tDriverState is %d\n", DriverState);
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_EMERG "Hello world exit!\n");
        platform_driver_unregister(&hello_driver);
}

module_init(hello_init);
module_exit(hello_exit);
leds_module

 

linuxGPIO申請函數和賦值函數

gpio_request
gpio_set_value
三星平臺配置GPIO函數
s3c_gpio_cfgpin
GPIO配置輸出模式的宏變量
S3C_GPIO_OUTPUT
使用這些函數和宏變量,將devicenode_linux_module.c改寫爲leds.c

將內核中的LED驅動去掉,從新編譯內核,燒寫到開發板。
invoke_hello.c改寫爲invoke_leds.c
簡單修改編譯文件,編譯
arm-none-linux-gnueabi-gcc -o invoke_leds invoke_leds.c -static

 

在使用invoke_leds時,內需須要從新編譯,去掉led的模塊。

[root@iTOP-4412]# insmod hello.ko                                                                          
[  149.082155] Hello world enter!
[  149.084082]  initialized
[  149.089124]  DriverState is 0

[root@iTOP-4412]# ls /dev/h*                                                                               
/dev/hello_ctl

[root@iTOP-4412]# ./invok_leds                                                                             
[  342.437287] hello open
[  342.438727] cmd is 1, arg is 1
APP open /dev/hello_ctl success
[  345.441525] cmd is 0, arg is 1
[  348.443325] cmd is 1, arg is 1
[  351.445052] cmd is 0, arg is 1
[  351.446632] hello release

 

3、GPIO複用和程序簡單分析

 去掉佔用GPIO的驅動

去掉佔用調用的GPIO驅動,包括ledsbuzzercamera ov5640WIFI mt6620

VIDEO_OV5640
Device Drivers
Multimedia support(MEDIA_SUPPORT [=y])
Video capture adapters(VIDEO_CAPTURE_DRIVERS [=y])(去掉)

MTK_COMBO_CHIP_MT6620
Device Drivers
MediaTek Connectivity Combo Chip Config
MediaTek Connectivity Combo Chip Support (MTK_COMBO [=y])(去掉)
Select Chip (<choice> [=y])

Enable LEDS config
Device Drivers
Character devices
Enable LEDS config

Enable BUZZER config
Device Drivers
Character devices
Enable BUZZER config

static int led_gpios[] = {
    EXYNOS4_GPL2(0), EXYNOS4_GPK(1),    /* Led IO 2個 */
    EXYNOS4_GPD0(0),              /* BUZZER IO 1個 */

    EXYNOS4_GPX1(0), EXYNOS4_GPX1(3),EXYNOS4_GPX1(5),EXYNOS4_GPX1(6),    /* 矩陣健盤8個 */
    EXYNOS4_GPX3(0),EXYNOS4_GPX2(6),EXYNOS4_GPX2(7),EXYNOS4_GPX3(5),

    EXYNOS4212_GPJ1(3),EXYNOS4_GPL0(1),EXYNOS4_GPL0(3),EXYNOS4212_GPJ1(0),      /* 攝像頭14個 */
    EXYNOS4212_GPJ1(2),EXYNOS4212_GPJ1(1),EXYNOS4212_GPJ0(7),EXYNOS4212_GPJ0(6),
    EXYNOS4212_GPJ0(5),EXYNOS4212_GPJ0(4),EXYNOS4212_GPJ0(0),EXYNOS4212_GPJ0(3),
   EXYNOS4212_GPJ0(1),EXYNOS4212_GPJ0(2),

    EXYNOS4_GPK3(6),EXYNOS4_GPK3(1),EXYNOS4_GPK3(4),EXYNOS4_GPK3(0),      /* WIFI 7個 */
    EXYNOS4_GPK3(3),EXYNOS4_GPK3(5),EXYNOS4_GPC1(1),
};

 

編譯簡單程序測試

leds.c修改成gpios.c,能夠控制32GPIO
修改Makefile文件
invoke_leds.c修改成invoke_gpios.c
編譯應用
arm-none-linux-gnueabi-gcc -o invoke_gpios invoke_gpios.c -static
在開發板上加載測試

#include <linux/init.h>
#include <linux/module.h>

/*驅動註冊的頭文件,包含驅動的結構體和註冊和卸載的函數*/
#include <linux/platform_device.h>
/*註冊雜項設備頭文件*/
#include <linux/miscdevice.h>
/*註冊設備節點的文件結構體*/
#include <linux/fs.h>

/*Linux中申請GPIO的頭文件*/
#include <linux/gpio.h>
/*三星平臺的GPIO配置函數頭文件*/
/*三星平臺EXYNOS系列平臺,GPIO配置參數宏定義頭文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
/*三星平臺4412平臺,GPIO宏定義頭文件*/
#include <mach/gpio-exynos4.h>

#define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_gpio"


MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");




/*led的兩個IO,網絡是KP_COL0,VDD50_EN*/
/*蜂鳴器的1個IO,網絡是MOTOR_PWM*/

/*矩陣鍵盤的8個IO,網絡是CHG_FLT,HOOK_DET,CHG_UOK,XEINT14_BAK,
GM_INT1,6260_GPIO1,CHG_COK,XEINT29/KP_ROW13/ALV_DBG25*/

/*攝像頭的14個IO,網絡是CAM_MCLK,CAM2M_RST,CAM2M_PWDN,
CAM_D5,CAM_D7,CAM_D6,CAM_D4,CAM_D3,CAM_D2,CAM_D1,
CAM_PCLK,CAM_D0,CAM_VSYNC,CAM_HREF。
I2C_SDA7,I2C_SCL7也是能夠設置爲GPIO,不過總線通常不要去動它*/

/*WIFI模塊的7個IO,WIFI_D3,WIFI_CMD,WIFI_D1,WIFI_CLK,WIFI_D0,WIFI_D2,GPC1_1*/
/*串口RX和TX等也是能夠設置爲GPIO,通常不要動它*/

/*數組中有32個引出到端子或者模塊的IO,還有相似sd卡等也是能夠做爲GPIO,
其它引到鏈接器可是沒有使用的GPIO等等*/
/*SCP管腳編號和POP的稍微有點不一樣,下面是SCP的*/
static int led_gpios[] = {
    EXYNOS4_GPL2(0),EXYNOS4_GPK1(1),
    EXYNOS4_GPD0(0),
    
    EXYNOS4_GPX1(0),EXYNOS4_GPX1(3),EXYNOS4_GPX1(5),EXYNOS4_GPX1(6),
    EXYNOS4_GPX3(0),EXYNOS4_GPX2(6),EXYNOS4_GPX2(7),EXYNOS4_GPX3(5),
    
    EXYNOS4212_GPJ1(3),EXYNOS4_GPL0(1),EXYNOS4_GPL0(3),EXYNOS4212_GPJ1(0),
    EXYNOS4212_GPJ1(2),EXYNOS4212_GPJ1(1),EXYNOS4212_GPJ0(7),EXYNOS4212_GPJ0(6),
    EXYNOS4212_GPJ0(5),EXYNOS4212_GPJ0(4),EXYNOS4212_GPJ0(0),EXYNOS4212_GPJ0(3),
    EXYNOS4212_GPJ0(1),EXYNOS4212_GPJ0(2),
    
    EXYNOS4_GPK3(6),EXYNOS4_GPK3(1),EXYNOS4_GPK3(4),EXYNOS4_GPK3(0),
    EXYNOS4_GPK3(3),EXYNOS4_GPK3(5),EXYNOS4_GPC1(1),
};

#define LED_NUM        ARRAY_SIZE(led_gpios)


static long hello_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
    printk("cmd is %d,arg is %d\n",cmd,arg);
    
    switch(cmd)
    {
        case 0:
        case 1:
            if (arg > LED_NUM) {
                return -EINVAL;
            }

            gpio_set_value(led_gpios[arg], cmd);
            break;

        default:
            return -EINVAL;
    }
    
    gpio_set_value(led_gpios[2], 0);
    
    return 0;
}

static int hello_release(struct inode *inode, struct file *file){
    printk(KERN_EMERG "hello release\n");
    return 0;
}

static int hello_open(struct inode *inode, struct file *file){
    printk(KERN_EMERG "hello open\n");
    return 0;
}

static struct file_operations hello_ops = {
    .owner = THIS_MODULE,
    .open = hello_open,
    .release = hello_release,
    .unlocked_ioctl = hello_ioctl,
};

static  struct miscdevice hello_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &hello_ops,
};


static int hello_probe(struct platform_device *pdv){
    int ret,i;
    
    printk(KERN_EMERG "\tinitialized\n");
    
    for(i=0; i<LED_NUM; i++)
    {
        ret = gpio_request(led_gpios[i], "LED");
        if (ret) {
            printk("%s: request GPIO %d for LED failed, ret = %d\n", DRIVER_NAME,
                    i, ret);
            }
        else{
            s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
            gpio_set_value(led_gpios[i], 1);            
        }
    }

    gpio_set_value(led_gpios[2], 0);
    
    misc_register(&hello_dev);
    if(ret<0)
    {
        printk("leds:register device failed!\n");
        goto exit;
    }
    return 0;

exit:
    misc_deregister(&hello_dev);
    return ret;
    return 0;
}

static int hello_remove(struct platform_device *pdv){
    int i;
    
    printk(KERN_EMERG "\tremove\n");
    
    for(i=0; i<LED_NUM; i++)
    {
        gpio_free(led_gpios[i]);
    }
    
    misc_deregister(&hello_dev);
    return 0;
}

static void hello_shutdown(struct platform_device *pdv){
    
    ;
}

static int hello_suspend(struct platform_device *pdv,pm_message_t pmt){
    
    return 0;
}

static int hello_resume(struct platform_device *pdv){
    
    return 0;
}

struct platform_driver hello_driver = {
    .probe = hello_probe,
    .remove = hello_remove,
    .shutdown = hello_shutdown,
    .suspend = hello_suspend,
    .resume = hello_resume,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
    }
};


static int hello_init(void)
gpios.c

invok_gpios.c

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <string.h>

#define GPIOS 32


int main(int argc , char **argv){
    int fd,i,cmd=2;
    char *hello_node = "/dev/hello_gpio";
    char *cmd0 = "0"; 
    char *cmd1 = "1";
    
    printf("argv[0] is %s;argv[1] is %s;",argv[0],argv[1]);
    
    if(strcmp(argv[1], cmd0) == 0){
        cmd = 0;
        printf("cmd is 0!\n");
    }
    if(strcmp(argv[1], cmd1) == 0){
        cmd = 1;
        printf("cmd is 1!\n");
    }
    
/*O_RDWR只讀打開,O_NDELAY非阻塞方式*/    
    if((fd = open(hello_node,O_RDWR|O_NDELAY))<0){
        printf("APP open %s failed!\n",hello_node);
    }
    else{
        printf("APP open %s success!\n",hello_node);
        for(i=0;i<GPIOS;i++){
            ioctl(fd,cmd,i);
            printf("APP ioctl %s ,cmd is %d,i is %d!\n",hello_node,cmd,i);
        }
    }
    
    close(fd);
}
invok_gpios

 

字符類GPIOS,LED驅動編寫

字符驅動程序:

#include <linux/init.h>
#include <linux/module.h>

/* define module_param module_param_array header file */
#include <linux/moduleparam.h>
/* define perm's head file*/
#include <linux/stat.h>
/* char device register head file */
#include <linux/fs.h>
/* MKDEV change device ID type */
#include <linux/kdev_t.h>
/* define char device struct */
#include <linux/cdev.h>
/* define memroy sapce */
#include <linux/slab.h>

/* include device_create class file */
#include <linux/device.h>

#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
#include <mach/gpio-exynos4.h>

#define DEVICE_NAME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");

int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;

/* input major device ID */
module_param(numdev_major, int, S_IRUSR);
/* input minor device ID */
module_param(numdev_minor, int, S_IRUSR);

static struct class *my_class;

struct reg_dev {
    char *data;
    unsigned long size;
    struct cdev cdev;
};
struct reg_dev *my_devices;

/* open */
static int chardevnode_open(struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "chardevnode open is success!\n");
    return 0;
}

/* close */
static int chardevnode_release(struct inode *indoe, struct file *file)
{
    printk(KERN_EMERG "chardevnode release is success!\n");
    return 0;
}

/* io control */
static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    printk(KERN_EMERG "chardevnode release is success!cmd is %d,arg is %d\n", cmd, arg);

    if(cmd >1 || arg> 1) {
        printk(KERN_EMERG "cmd and arg is 0 or 1");
        return 0;
    }
    switch(arg) {
        case 0:
            gpio_set_value(EXYNOS4_GPL2(0), cmd);
            break;
        case 1:
            gpio_set_value(EXYNOS4_GPK1(1), cmd);
            break;
        default:
            printk(KERN_EMERG "cmd and arg is 0 or 1");
            break;
    }

    return 0;
}

/* read */
static ssize_t chardevnode_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
{
    return 0;
}

/* write */
static ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t size, loff_t *ops)
{
    return 0;
}

/* lseek */
static loff_t chardevnode_llseek(struct file *file, loff_t offset, int whence)
{
    return 0;
}

struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open  = chardevnode_open,
    .release = chardevnode_release,
    .unlocked_ioctl = chardevnode_ioctl,
    .read = chardevnode_read,
    .write = chardevnode_write,
    .llseek = chardevnode_llseek,
};

/* GPL2_0 */
static int led1_init(void)
{
    int ret;
    printk(KERN_EMERG "Gpio led 1 init\n");

    ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");
    if(ret < 0) {
        printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed\n");
        return ret;
    }

    s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);

    gpio_set_value(EXYNOS4_GPL2(0), 0);

    return 0; 
}

/* GPK1_1 */
static int led2_init(void)
{
    int ret;
    printk(KERN_EMERG "GPIO led 2 init\n");

    ret = gpio_request(EXYNOS4_GPK1(1), "LEDS2");
    if(ret < 0) {
        printk(KERN_EMERG "gpio_request EXYNOS4_GPK1(1) fialed\n");
        return ret;
    }
    s3c_gpio_cfgpin(EXYNOS4_GPK1(1), S3C_GPIO_OUTPUT);

    gpio_set_value(EXYNOS4_GPK1(1), 0);

    return 0;
}

static void reg_init_cdev(struct reg_dev *dev, int index)
{
    int err;
    int devno = MKDEV(numdev_major, numdev_minor+index);

    cdev_init(&dev->cdev, &my_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &my_fops;

    err = cdev_add(&dev->cdev, devno, 1);
    if(err) {
        printk(KERN_EMERG "cdev_add %d is fail! %d\n", index, err);
    } else {
        printk(KERN_EMERG "cdev_add %d is success!\n", (numdev_minor+index));
    }
}

static int hello_init(void)
{
    int ret, i;
    dev_t num_dev;
    
    printk(KERN_EMERG "numdev_major is %d!\n", numdev_major);
    printk(KERN_EMERG "numdev_minor is %d!\n", numdev_minor);

    if(numdev_major) {
        num_dev = MKDEV(numdev_major, numdev_minor);
        ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
    } else {
        ret = alloc_chrdev_region(&num_dev, numdev_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
        numdev_major = MAJOR(num_dev);
        printk(KERN_EMERG "register req major number is %d\n", numdev_major);
    }

    if(ret < 0) {
        printk(KERN_EMERG "register_chrdev_region req %d is failed\n", numdev_major);
        unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
        return ret;
    }

    my_class = class_create(THIS_MODULE, DEVICE_NAME);    
    my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev), GFP_KERNEL);
    if(!my_devices) {
        ret = -ENOMEM;
        printk(KERN_EMERG "kmalloc fialed!\n");
        goto fail;
    }
    memset(my_devices, 0, DEVICE_MINOR_NUM*sizeof(struct reg_dev));
    for(i=0;i<DEVICE_MINOR_NUM;i++) {
        my_devices[i].data = kmalloc(REGDEV_SIZE, GFP_KERNEL);        
        memset(my_devices[i].data, 0, REGDEV_SIZE);        /* data address */
        /* register device to system */
        reg_init_cdev(&my_devices[i], i);
        /* create device node */
        device_create(my_class, NULL, MKDEV(numdev_major, numdev_minor+i), "NULL", DEVICE_NAME"%d", i);
    }

    led1_init();
    led2_init();
    printk(KERN_EMERG "Hello World enter!\n");
    return 0;

fail:
    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
    return ret;
}

static void hello_exit(void)
{
    int i;
    dev_t num_dev = MKDEV(numdev_major, numdev_minor);
    printk(KERN_EMERG "Hello World exit!\n");
    for(i=0;i<DEVICE_MINOR_NUM;i++) {
        cdev_del(&my_devices[i].cdev);
        /* release memory*/
        device_destroy(my_class, MKDEV(numdev_major, numdev_minor+i));
    }
    
    /* release my class*/
    class_destroy(my_class);
    /* release kfre */
    kfree(my_devices);
    unregister_chrdev_region(num_dev, DEVICE_MINOR_NUM);
}

module_init(hello_init);
module_exit(hello_exit);
char_driver_leds.c

而後是應用程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
        int fd0, fd1;
        char *hello_node0 = "/dev/chardevnode0";
        char *hello_node1 = "/dev/chardevnode1";
        if(argc > 2) {
                printf("please input cmd and arg\n");
        }
        /* O_RDWR只讀打開, O_NDELAY非阻塞方式 */
        fd0 = open(hello_node0, O_RDWR|O_NDELAY);
        if(fd0 < 0) {
                printf("APP open %s failed\n", hello_node0);
                exit(EXIT_FAILURE);
        } else {
                printf("APP open %s success\n", hello_node0);
                ioctl(fd0, atoi(argv[1]), atoi(argv[2]));
        }
        /* O_RDWR只讀打開, O_NDELAY非阻塞方式 */
/*
        fd1 = open(hello_node0, O_RDWR|O_NDELAY);
        if(fd1 < 0) {
                printf("APP open %s failed\n", hello_node0);
                exit(EXIT_FAILURE);
        } else {
                printf("APP open %s success\n", hello_node0);
                ioctl(fd1, 1, 6);
        }
*/
        close(fd0);
        close(fd1);
}
invoke_cahr_driver.c

而後是makefile

TARGET_NAME = char_driver_leds
APP_NAME = invoke_char_gpios
obj-m += $(TARGET_NAME).o

KDIR := /home/topeet/chen/kernel-3.0/iTop4412_Kernel_3.0

PWD ?= $(shell pwd)

all:app
        make -C $(KDIR) M=$(PWD) modules

app:$(APP_NAME)
        arm-none-linux-gnueabi-gcc $(APP_NAME).c -o $(APP_NAME) -static

clean:
        rm -rf *.o *.ko *.mod.c *.symvers *.order *.cmd .$(TARGET_NAME)* $(APP_NAME)
Makefile

測試結果:

[root@iTOP-4412]# insmod char_driver_leds.ko                                                               
[  420.107938] numdev_major is 0!
[  420.109549] numdev_minor is 0!
[  420.112677] register req major number is 248
[  420.125765] cdev_add 0 is success!
[  420.137424] cdev_add 1 is success!
[  420.148881] Gpio led 1 init
[  420.150342] gpio_request EXYNOS4_GPL2(0) failed
[  420.154743] GPIO led 2 init
[  420.165167] Hello World enter!
[root@iTOP-4412]# ./invoke_char_gpios 1 0                                                                  
please input cmd [  431.050669] chardevnode open is success!
[  431.054691] chardevnode release is success!cmd is 1,arg is 0
[  431.060238] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success
[root@iTOP-4412]# ./invoke_char_gpios 1 1                                                                  
please input cmd [  435.289936] chardevnode open is success!
[  435.294047] chardevnode release is success!cmd is 1,arg is 1
[  435.299498] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success
[root@iTOP-4412]# ./invoke_char_gpios 0 0                                                                  
please input cmd [  440.595232] chardevnode open is success!
[  440.599237] chardevnode release is success!cmd is 0,arg is 0
and arg
APP open /dev/chardevnode0 success
[  440.609648] chardevnode release is success!
[root@iTOP-4412]# ./invoke_char_gpios 0 1                                                                  
please input cmd [  443.313565] chardevnode open is success!
[  443.317679] chardevnode release is success!cmd is 0,arg is 1
[  443.323129] chardevnode release is success!
and arg
APP open /dev/chardevnode0 success

[root@iTOP-4412]# rmmod char_driver_leds                                                                   
[  468.722834] Hello World exit!
測試結果
相關文章
相關標籤/搜索