• 在內核源碼目錄下使用命令「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)
• 初始化函數調用了「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被分爲4個bank,這
個和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-cfg.h中s3c_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是極大的浪費。把內部寄存器也當作「特殊的物理地址」便可。
• 只講了虛擬地址和物理地址對應數組,怎麼沒介紹哪裏調用了?
– 你們能夠看一下函數ioremap,linux會調用這個函數來實現gpio的映射關係
– 今天講的已經夠多夠深刻了,你們只要可以理解這麼一層意思就能夠了,這個東西對咱們實際寫驅動的幫助其實不是那麼大!
• 若是我仍是理解不了「對宏定義EXYNOS4_GPL2(0)的操做就是對4412芯片管腳AC21寄存器的操做」,怎麼辦?
– 記住這個結論,可以將宏變量EXYNOS4_GPL2(0)和GPL這一組GPIO的第0位寄存器聯想起來。
– 後面跟着我依葫蘆畫瓢,不影響你們實際寫程序,有興趣再回過頭理解
• 三極管(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);
– 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
去掉佔用調用的GPIO驅動,包括leds,buzzer,camera ov5640,WIFI 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,能夠控制32個GPIO
• 修改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)
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); }
字符驅動程序:
#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);
而後是應用程序:
#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); }
而後是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)
測試結果:
[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!