字符设备驱动程序设计思路 基于platform平台的设备驱动的关键在于,1)将驱动程序分为与具体硬件驱动分为上下两层,2)并将下层的具体硬件驱动分离为资源和相关的SOC驱动。本文为便于理解分层、分离的概念,将驱动 分成了3个C文件:button_drv.c、imx6_io_drv.c、atk_btn_dev.c以及2个头文件:button_drv.h、imx6_io_drv.h。下面分别列出代码按键设备驱动(框架)文件 button_drv.c文件实现与硬件有关的按键驱动,主要实现struct file_operations类型相关函数及其实例的注册/解除注册。其中的.read、.open、.release成员要调用下层驱动:
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/gpio.h>
# include <linux/cdev.h>
# include <asm/mach/map.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include "button_drv.h"
struct button_drv btn_drv = {
. hw_ops = NULL ,
. name = "gbtn" ,
. major = 0 ,
} ;
ssize_t button_drv_read ( struct file * filp, char __user * buf, size_t size, loff_t * pos)
{
int ret;
int minor = iminor ( filp-> f_inode) ;
struct btn_hw_ops * ops = ( struct btn_hw_ops * ) filp-> private_data;
char kval;
size = ( size >= 1 ) ? 1 : 0 ;
if ( ops == NULL ) {
printk ( "Please register button hardware operations instance %s %s, %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return - EIO;
}
if ( minor >= ops-> num) {
printk ( "Reading button value error %s %s, %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return - EINVAL;
}
if ( ops-> read ( minor, & kval) == 0 ) {
ret = copy_to_user ( buf, & kval, size) ;
printk ( "Read button value successfully:" ) ;
return size;
}
return 0 ;
}
int button_drv_open ( struct inode * nd, struct file * filp)
{
int minor = iminor ( nd) ;
struct btn_hw_ops * ops = btn_drv. hw_ops;
filp-> private_data = btn_drv. hw_ops;
if ( ops == NULL ) {
printk ( "Please register button hardware operations instance %s %s, %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return - EIO;
}
if ( minor >= ops-> num) {
printk ( "Openning button driver error %s %s, %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return - EINVAL;
}
if ( ops-> open ( minor) == 0 )
printk ( "Open button driver successfully:" ) ;
return 0 ;
}
int button_drv_release ( struct inode * nd, struct file * filp)
{
int minor = iminor ( nd) ;
struct btn_hw_ops * ops = ( struct btn_hw_ops * ) filp-> private_data;
if ( ops == NULL ) {
printk ( "Please register button hardware operations instance %s %s, %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return - EIO;
}
if ( minor >= ops-> num) {
printk ( "Closing button driver error %s %s, %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return - EINVAL;
}
if ( ops-> close ( minor) == 0 ) {
printk ( "Close button driver successfully:" ) ;
filp-> private_data = NULL ;
}
return 0 ;
}
unsigned int button_drv_poll ( struct file * filp, struct poll_table_struct * wait)
{
return 0 ;
}
static struct file_operations button_drv_ops = {
. owner = THIS_MODULE,
. read = button_drv_read,
. open = button_drv_open,
. release = button_drv_release,
. poll = button_drv_poll,
} ;
static int __init button_drv_init ( void )
{
btn_drv. major = register_chrdev ( btn_drv. major, btn_drv. name, & button_drv_ops) ;
btn_drv. class = class_create ( THIS_MODULE, btn_drv. name) ;
if ( IS_ERR ( btn_drv. class) ) {
printk ( "Class create error!\n" ) ;
unregister_chrdev ( btn_drv. major, btn_drv. name) ;
}
printk ( " %s %s line %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
return 0 ;
}
module_init ( button_drv_init) ;
static void __exit button_drv_exit ( void )
{
class_destroy ( btn_drv. class) ;
unregister_chrdev ( btn_drv. major, btn_drv. name) ;
printk ( " %s %s line %d\n" , __FILE__ , __FUNCTION__, __LINE__ ) ;
}
module_exit ( button_drv_exit) ;
int button_hw_ops_register ( struct btn_hw_ops * ops)
{
if ( btn_drv. hw_ops)
return - EIO;
btn_drv. hw_ops = ops;
printk ( "Register button hardware operations instance successfully\n" ) ;
return 0 ;
}
EXPORT_SYMBOL ( button_hw_ops_register) ;
void button_hw_ops_unregister ( struct btn_hw_ops * ops)
{
if ( btn_drv. hw_ops)
btn_drv. hw_ops = NULL ;
printk ( "Register button hardware operations instance successfully\n" ) ;
}
EXPORT_SYMBOL ( button_hw_ops_unregister) ;
void button_device_create ( int num)
{
int i;
for ( i = 0 ; i < num; i++ )
device_create ( btn_drv. class, NULL , MKDEV ( btn_drv. major, i) , NULL , "gbtn%d" , i) ;
}
EXPORT_SYMBOL ( button_device_create) ;
void button_device_destroy ( int num)
{
int i;
for ( i = 0 ; i < num; i++ )
device_destroy ( btn_drv. class, MKDEV ( btn_drv. major, i) ) ;
}
EXPORT_SYMBOL ( button_device_destroy) ;
MODULE_AUTHOR ( "glen" ) ;
MODULE_LICENSE ( "GPL" ) ;
其中包含的button_drv.h文件提供了与下层soc驱动共用的结构体,注册/销毁soc驱动等接口函数,以便在button_drv.c文件中调用——驱动上层和下层都依懒于接口。
# ifndef __BUTTON_DRV_H__
# define __BUTTON_DRV_H__
struct btn_hw_ops {
u32 num;
int ( * read) ( int idx, char * kval) ;
int ( * open) ( int idx) ;
int ( * close) ( int idx) ;
} ;
struct button_drv {
struct btn_hw_ops * hw_ops;
struct class * class;
char * name;
int major;
} ;
int button_hw_ops_register ( struct btn_hw_ops * ops) ;
void button_hw_ops_unregister ( struct btn_hw_ops * ops) ;
void button_device_create ( int num) ;
void button_device_destroy ( int num) ;
# endif
SOC相关的IO驱动文件 imx6_io_drv.c获取IO资源(这里获取的是按键的IO信息),并将资源用于实现按键的open、read、close等操作:
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/gpio.h>
# include <linux/cdev.h>
# include <linux/platform_device.h>
# include <asm/mach/map.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include "button_drv.h"
# include "imx6_io_drv.h"
static struct btn_dev_res __btn_dev_res[ 16 ] ;
static int imx6_io_read ( int idx, char * kval) ;
static int imx6_io_open ( int idx) ;
static int imx6_io_close ( int idx) ;
static struct btn_hw_ops btn_hw_oper = {
. open = imx6_io_open,
. read = imx6_io_read,
. close = imx6_io_close,
} ;
static int imx6_io_read ( int idx, char * kval)
{
int val = 0 ;
struct btn_dev_res * pbtn;
if ( idx >= btn_hw_oper. num)
return - EINVAL;
pbtn = & __btn_dev_res[ idx] ;
val = readl ( pbtn-> vir_addr. gpio_dr) ;
if ( pbtn-> active_level) {
if ( val & ( 1 << pbtn-> pin_num) )
* kval = 1 ;
else
* kval = 0 ;
} else {
if ( val & ( 1 << pbtn-> pin_num) )
* kval = 0 ;
else
* kval = 1 ;
}
return 0 ;
}
static int imx6_io_open ( int idx)
{
int val = 0 ;
struct btn_dev_res * pbtn;
if ( idx >= btn_hw_oper. num)
return - EINVAL;
pbtn = & __btn_dev_res[ idx] ;
pbtn-> vir_addr. ccm_ccgr = ioremap ( ( phys_addr_t ) ( pbtn-> phy_addr. ccm_ccgr) , 4 ) ;
pbtn-> vir_addr. sw_mux = ioremap ( ( phys_addr_t ) ( pbtn-> phy_addr. sw_mux ) , 4 ) ;
pbtn-> vir_addr. sw_pad = ioremap ( ( phys_addr_t ) ( pbtn-> phy_addr. sw_pad ) , 4 ) ;
pbtn-> vir_addr. gpio_dr = ioremap ( ( phys_addr_t ) ( pbtn-> phy_addr. gpio_dr ) , 4 ) ;
pbtn-> vir_addr. gpio_dir = ioremap ( ( phys_addr_t ) ( pbtn-> phy_addr. gpio_dir) , 4 ) ;
val = readl ( pbtn-> vir_addr. ccm_ccgr) ;
val |= ( 3 << pbtn-> clk_bits) ;
writel ( val, pbtn-> vir_addr. ccm_ccgr) ;
val = readl ( pbtn-> vir_addr. sw_mux) ;
val &= ~ ( 0x0f ) ;
val |= 5 ;
writel ( val, pbtn-> vir_addr. sw_mux) ;
writel ( 0x10B0 , pbtn-> vir_addr. sw_pad) ;
val = readl ( pbtn-> vir_addr. gpio_dir) ;
val &= ~ ( 1 << pbtn-> pin_num) ;
writel ( val, pbtn-> vir_addr. gpio_dir) ;
printk ( "pbtn->phy_addr.ccm_ccgr = %x \n" , ( phys_addr_t ) ( pbtn-> phy_addr. ccm_ccgr) ) ;
return 0 ;
}
static int imx6_io_close ( int idx)
{
struct btn_dev_res * pbtn;
if ( idx >= btn_hw_oper. num)
return - EINVAL;
pbtn = & __btn_dev_res[ idx] ;
iounmap ( pbtn-> vir_addr. ccm_ccgr) ;
iounmap ( pbtn-> vir_addr. sw_mux ) ;
iounmap ( pbtn-> vir_addr. sw_pad ) ;
iounmap ( pbtn-> vir_addr. gpio_dr ) ;
iounmap ( pbtn-> vir_addr. gpio_dir) ;
return 0 ;
}
int btn_hw_drv_probe ( struct platform_device * pdev)
{
int i = 0 ;
struct resource * res;
while ( 1 ) {
res = platform_get_resource ( pdev, IORESOURCE_IRQ, i) ;
if ( res == NULL )
break ;
memcpy ( & __btn_dev_res[ i] , ( const void * ) res-> start, sizeof ( struct btn_dev_res ) ) ;
i++ ;
}
if ( i == 0 ) {
dev_err ( & pdev-> dev, "Cannot find IO resource\n" ) ;
return - ENOENT;
}
btn_hw_oper. num = i;
button_hw_ops_register ( & btn_hw_oper) ;
button_device_create ( btn_hw_oper. num) ;
return 0 ;
}
int btn_hw_drv_remove ( struct platform_device * pdev)
{
button_hw_ops_unregister ( NULL ) ;
button_device_destroy ( btn_hw_oper. num) ;
btn_hw_oper. num = 0 ;
return 0 ;
}
static struct platform_driver btn_hw_drv = {
. driver = {
. name = "gbtn" ,
} ,
. probe = btn_hw_drv_probe,
. remove = btn_hw_drv_remove,
} ;
static int __init imx6_io_drv_init ( void )
{
int ret;
ret = platform_driver_register ( & btn_hw_drv) ;
return ret;
}
module_init ( imx6_io_drv_init) ;
static void __exit imx6_io_drv_exit ( void )
{
platform_driver_unregister ( & btn_hw_drv) ;
}
module_exit ( imx6_io_drv_exit) ;
MODULE_LICENSE ( "GPL" ) ;
MODULE_AUTHOR ( "glen" ) ;
其中包含的头文件button_drv.h和imx6_io_drv.h,前者上面已说明不再赘述;后者为soc驱动和资源文件提供按键设备资源结构体:
# ifndef __IMX6_IO_DRV_H__
# define __IMX6_IO_DRV_H__
# include <linux/types.h>
struct io_reg {
void * ccm_ccgr;
void * sw_mux ;
void * sw_pad ;
void * gpio_dr ;
void * gpio_dir;
} ;
struct btn_dev_res {
struct io_reg phy_addr;
struct io_reg vir_addr;
u8 active_level;
u8 clk_bits;
u8 pin_num;
} ;
# endif
按键相关资源文件 atk_btn_dev.c提供了具体目标板的按键相关资源:
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/gpio.h>
# include <linux/cdev.h>
# include <asm/mach/map.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include <linux/platform_device.h>
# include "imx6_io_drv.h"
static struct btn_dev_res atk_btn_res[ ] = {
{
. phy_addr = {
. ccm_ccgr = ( 0x020C406C ) ,
. sw_mux = ( 0x020E008C ) ,
. sw_pad = ( 0x020E0318 ) ,
. gpio_dr = ( 0x0209C000 ) ,
. gpio_dir = ( 0x0209C004 ) ,
} ,
. active_level = 0 ,
. clk_bits = 26 ,
. pin_num = 18 ,
} , {
. phy_addr = {
. ccm_ccgr = ( 0x020C406C ) ,
. sw_mux = ( 0x020E0068 ) ,
. sw_pad = ( 0x020E02F4 ) ,
. gpio_dr = ( 0x0209C000 ) ,
. gpio_dir = ( 0x0209C004 ) ,
} ,
. active_level = 1 ,
. clk_bits = 26 ,
. pin_num = 3 ,
} ,
} ;
static struct resource btn_res[ ] = {
{
. name = "pin_res" ,
. start = ( resource_size_t ) & atk_btn_res[ 0 ] ,
. flags = IORESOURCE_IRQ,
} , {
. name = "pin_res" ,
. start = ( resource_size_t ) & atk_btn_res[ 1 ] ,
. flags = IORESOURCE_IRQ,
} ,
} ;
static struct platform_device atk_btn_dev = {
. name = "gbtn" ,
. resource = btn_res,
. num_resources = ARRAY_SIZE ( btn_res) ,
} ;
static int __init atk_btn_dev_init ( void )
{
platform_device_register ( & atk_btn_dev) ;
return 0 ;
}
module_init ( atk_btn_dev_init) ;
static void __exit atk_btn_dev_exit ( void )
{
platform_device_unregister ( & atk_btn_dev) ;
}
module_exit ( atk_btn_dev_exit) ;
MODULE_LICENSE ( "GPL" ) ;
MODULE_AUTHOR ( "glen" ) ;
应用程序
# include "stdio.h"
# include "unistd.h"
# include "sys/types.h"
# include "sys/stat.h"
# include "fcntl.h"
# include "stdlib.h"
# include "string.h"
int main ( int argc, char * argv[ ] )
{
int fd, ret;
char * filename;
char kval;
if ( argc != 2 ) {
printf ( "Error Usage!\r\n" ) ;
return - 1 ;
}
filename = argv[ 1 ] ;
fd = open ( filename, O_RDWR) ;
if ( fd < 0 ) {
printf ( "Can't open file %s\r\n" , filename) ;
return - 1 ;
}
while ( 1 ) {
ret = read ( fd, & kval, 1 ) ;
if ( ret <= 0 ) {
printf ( "Read glen button value failed!\r\n" ) ;
close ( fd) ;
return - 1 ;
} else {
printf ( "The glen button value is: %d!\r\n" , kval) ;
}
sleep ( 1 ) ;
}
ret = close ( fd) ;
if ( ret < 0 ) {
printf ( "file %s close failed!\r\n" , argv[ 1 ] ) ;
return - 1 ;
}
return 0 ;
}
在alientek_linux_alpha开发板实测验证
/home/glen/linux/imx6ull/glen/3_1_button/button_drv.c button_drv_exit line 130
/drv_module
/home/glen/linux/imx6ull/glen/3_1_button/button_drv.c button_drv_init line 120
/drv_module
/drv_module
Register button hardware operations instance successfully
/drv_module
pbtn-> phy_addr.ccm_ccgr = 20c406c
Open button driver successfully:Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 0 !
Read button value successfully:The glen button value is: 1 !
Read button value successfully:The glen button value is: 1 !
Read button value successfully:The glen button value is: 1 !
Read button value successfully:The glen button value is: 1 !
Read button value successfully:The glen button value is: 1 !