linux 驱动笔记(四)

来源:互联网 发布:java的编译命令是哪个 编辑:程序博客网 时间:2024/05/16 04:58

第六章 GPIO的标准接口函数

1 什么是GPIO的标准接口函数

思考:

1.1设计GPIO驱动的方法???

 

1.1.1 找到配置/控制GPIO的寄存器,得到了访问该寄存器的物理地址

1.1.2 申请SFR的物理内存区

1.1.3 IO内存的动态映射,由物理地址得到虚拟地址

1.1.4 通过虚拟地址设置寄存器

 

 

1.2有没有简单的方法??

 

应为GPIO 中断 时钟....在嵌入式平台上都是非常常用的模块。这样linux内核将这个模块的控制封装成的函数,当这些封装好的函数的时候,大大简化程序的设计过程。

 

2 GPIO标准接口函数的用法

 

#include <linux/gpio.h>

 

2.1 GPIO的申请与释放

/* request GPIO, returning 0 or negative errno.

 

* non-null labels may be useful for diagnostics.

 

*/

int gpio_request(unsigned gpio, const char *label);

 

/* release previously-claimed GPIO */

void gpio_free(unsigned gpio);

参数说明:

unsigned gpio ---GPIO号,每个GPIO都有唯一的ID

const char *label ---自定义的GPIO的名字

 

2.2 设置GPIO的方向

int gpio_direction_input(unsigned gpio);

int gpio_direction_output(unsigned gpio, int value);

 

2.3 设置GPIO的输出值或者读取GPIO的输入值

/* GPIO INPUT:  return zero or nonzero */

int gpio_get_value(unsigned gpio);

 

/* GPIO OUTPUT */

void gpio_set_value(unsigned gpio, int value);

 

3 GPIO

GPIO口号是和处理器是相关的,不同处理器GPIO的数量 名字都是不一样的。

GPIO号应该是在一个头文件中定义的。

 

linux/arch/arm/mach-s5pv210/include/mach/gpio.h

 

/* S5PV210 GPIO number definitions */

#define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr))

#define S5PV210_GPA1(_nr) (S5PV210_GPIO_A1_START + (_nr))

#define S5PV210_GPB(_nr) (S5PV210_GPIO_B_START + (_nr))

#define S5PV210_GPC0(_nr) (S5PV210_GPIO_C0_START + (_nr))

#define S5PV210_GPC1(_nr) (S5PV210_GPIO_C1_START + (_nr))

#define S5PV210_GPD0(_nr) (S5PV210_GPIO_D0_START + (_nr))

#define S5PV210_GPD1(_nr) (S5PV210_GPIO_D1_START + (_nr))

#define S5PV210_GPE0(_nr) (S5PV210_GPIO_E0_START + (_nr))

#define S5PV210_GPE1(_nr) (S5PV210_GPIO_E1_START + (_nr))

#define S5PV210_GPF0(_nr) (S5PV210_GPIO_F0_START + (_nr))

#define S5PV210_GPF1(_nr) (S5PV210_GPIO_F1_START + (_nr))

#define S5PV210_GPF2(_nr) (S5PV210_GPIO_F2_START + (_nr))

#define S5PV210_GPF3(_nr) (S5PV210_GPIO_F3_START + (_nr))

#define S5PV210_GPG0(_nr) (S5PV210_GPIO_G0_START + (_nr))

#define S5PV210_GPG1(_nr) (S5PV210_GPIO_G1_START + (_nr))

#define S5PV210_GPG2(_nr) (S5PV210_GPIO_G2_START + (_nr))

#define S5PV210_GPG3(_nr) (S5PV210_GPIO_G3_START + (_nr))

#define S5PV210_GPH0(_nr) (S5PV210_GPIO_H0_START + (_nr))

#define S5PV210_GPH1(_nr) (S5PV210_GPIO_H1_START + (_nr))

#define S5PV210_GPH2(_nr) (S5PV210_GPIO_H2_START + (_nr))

#define S5PV210_GPH3(_nr) (S5PV210_GPIO_H3_START + (_nr))

#define S5PV210_GPI(_nr) (S5PV210_GPIO_I_START + (_nr))

#define S5PV210_GPJ0(_nr) (S5PV210_GPIO_J0_START + (_nr))

#define S5PV210_GPJ1(_nr) (S5PV210_GPIO_J1_START + (_nr))

#define S5PV210_GPJ2(_nr) (S5PV210_GPIO_J2_START + (_nr))

#define S5PV210_GPJ3(_nr) (S5PV210_GPIO_J3_START + (_nr))

#define S5PV210_GPJ4(_nr) (S5PV210_GPIO_J4_START + (_nr))

#define S5PV210_MP01(_nr) (S5PV210_GPIO_MP01_START + (_nr))

#define S5PV210_MP02(_nr) (S5PV210_GPIO_MP02_START + (_nr))

#define S5PV210_MP03(_nr) (S5PV210_GPIO_MP03_START + (_nr))

#define S5PV210_MP04(_nr) (S5PV210_GPIO_MP04_START + (_nr))

#define S5PV210_MP05(_nr) (S5PV210_GPIO_MP05_START + (_nr))

#define S5PV210_MP06(_nr) (S5PV210_GPIO_MP06_START + (_nr))

#define S5PV210_MP07(_nr) (S5PV210_GPIO_MP07_START + (_nr))

#define S5PV210_MP10(_nr) (S5PV210_GPIO_MP10_START + (_nr))

#define S5PV210_MP11(_nr) (S5PV210_GPIO_MP11_START + (_nr))

#define S5PV210_MP12(_nr) (S5PV210_GPIO_MP12_START + (_nr))

#define S5PV210_MP13(_nr) (S5PV210_GPIO_MP13_START + (_nr))

#define S5PV210_MP14(_nr) (S5PV210_GPIO_MP14_START + (_nr))

#define S5PV210_MP15(_nr) (S5PV210_GPIO_MP15_START + (_nr))

#define S5PV210_MP16(_nr) (S5PV210_GPIO_MP16_START + (_nr))

#define S5PV210_MP17(_nr) (S5PV210_GPIO_MP17_START + (_nr))

#define S5PV210_MP18(_nr) (S5PV210_GPIO_MP18_START + (_nr))

#define S5PV210_MP20(_nr) (S5PV210_GPIO_MP20_START + (_nr))

#define S5PV210_MP21(_nr) (S5PV210_GPIO_MP21_START + (_nr))

#define S5PV210_MP22(_nr) (S5PV210_GPIO_MP22_START + (_nr))

#define S5PV210_MP23(_nr) (S5PV210_GPIO_MP23_START + (_nr))

#define S5PV210_MP24(_nr) (S5PV210_GPIO_MP24_START + (_nr))

#define S5PV210_MP25(_nr) (S5PV210_GPIO_MP25_START + (_nr))

#define S5PV210_MP26(_nr) (S5PV210_GPIO_MP26_START + (_nr))

#define S5PV210_MP27(_nr) (S5PV210_GPIO_MP27_START + (_nr))

#define S5PV210_MP28(_nr) (S5PV210_GPIO_MP28_START + (_nr))

#define S5PV210_ETC0(_nr) (S5PV210_GPIO_ETC0_START + (_nr))

#define S5PV210_ETC1(_nr) (S5PV210_GPIO_ETC1_START + (_nr))

#define S5PV210_ETC2(_nr) (S5PV210_GPIO_ETC2_START + (_nr))

#define S5PV210_ETC4(_nr) (S5PV210_GPIO_ETC4_START + (_nr))

 

例:

LED1 --- GPJ2_0  --- S5PV210_GPJ2(0)

LED2 --- GPJ2_1  --- S5PV210_GPJ2(1)

LED3 --- GPJ2_2  --- S5PV210_GPJ2(2)

LED4 --- GPJ2_3  --- S5PV210_GPJ2(3)

 

BEEP --- GPD0_0  --- S5PV210_GPD0(0)

 

4 使用gpio标准接口函数设计驱动的思路

 

//char buf[2],buf[1]灯的状态:1--on0-->off

//            buf[0]哪一个led1/2/3/4

ssize_t gec210_led_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)

{

int ret;

char kbuf[2];

if(len != 2)

return -EINVAL;

ret = copy_from_user(kbuf,buf,len); //从用户空间拷贝数据

if(ret!= 0)

return -EFAULT;

if( (kbuf[0]<1) || (kbuf[0]>4) )

return -EINVAL;

gpio_set_value(unsigned gpio, int value);

else

return -EINVAL;

return len;

}

 

static struct file_operations gec210_led_fops = {

.owner = THIS_MODULE,

.write = gec210_led_write,

};

 

 

static int __init gec210_led_init(void) //驱动的初始化及安装函数

{

int gpio_request(unsigned gpio, const char *label);

int gpio_direction_output(unsigned gpio, int value);

 

}

 

static void __exit gec210_led_exit(void) //驱动卸载函数

{

gpio_free(unsigned gpio);

}

 

module_init(gec210_led_init); //驱动的入口

module_exit(gec210_led_exit); //驱动的出口

 

A代码一

1. filename: led_drv.c

 

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/uaccess.h>

#include <linux/ioport.h>

#include <linux/io.h>

#include <linux/device.h>

 

//1)定义一个字符设备cdev

static struct cdev led_drv;

 

static unsigned int led_major = 0; //0-->动态分配,>0-->静态注册

static unsigned int led_minor = 0;

static dev_t led_drv_num;

 

static struct resource *  gec210_led_res;

static unsigned int *gpj2con_va; //0xe0200280对应的虚拟地址指针

static unsigned int *gpj2dat_va; //0xe0200284对应的虚拟地址指针

 

static struct class *gec210_led_class;

static struct device *gec210_led_device;

 

 

 //3)定义文件操作集,并初始化

//char buf[2],buf[1]灯的状态:1--on0-->off

//            buf[0]哪一个led1/2/3/4

ssize_t gec210_led_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)

{

int ret;

char kbuf[2];

if(len != 2)

return -EINVAL;

ret = copy_from_user(kbuf,buf,len); //从用户空间拷贝数据

if(ret!= 0)

return -EFAULT;

if( (kbuf[0]<1) || (kbuf[0]>4) )

return -EINVAL;

if(kbuf[1]==1)

*gpj2dat_va &= ~(1<<(kbuf[0]-1));

else if(kbuf[1]==0)

*gpj2dat_va |= (1<<(kbuf[0]-1));

else

return -EINVAL;

return len;

}

 

  

static struct file_operations gec210_led_fops = {

.owner = THIS_MODULE,

.write = gec210_led_write,

};

 

static int __init gec210_led_init(void) //驱动的初始化及安装函数

{

int ret;

//2)申请/注册设备号

if(led_major == 0){

ret = alloc_chrdev_region(&led_drv_num, led_minor, 1, "gec210_leds");

}

else{

led_drv_num = MKDEV(led_major,led_minor);

ret = register_chrdev_region(led_drv_num,  1, "gec210_leds");

}

if(ret < 0){

printk("led_drv_num is error \n");

return ret;

}

 

//4)初始化cdev

cdev_init(&led_drv,  &gec210_led_fops);

 

//5)cdev加入kernel

ret = cdev_add(&led_drv,led_drv_num, 1 );

if(ret < 0){

printk("cdev add error\n");

goto failed_cdev_add;

}

 

//6)申请物理内存区,作为一个资源

gec210_led_res = request_mem_region(0xe0200280,8,"GPJ2_LED"); //cat /proc/iomem

if(gec210_led_res == NULL)

{

printk("requst mem region error\n");

ret = -EBUSY;

goto failed_request_mem_region;

}

//7)io内存动态映射

gpj2con_va = ioremap(0xe0200280,8);

if(gpj2con_va == NULL)

{

printk("ioremap error\n");

ret = -EFAULT;

goto failed_ioremap;

}

gpj2dat_va = gpj2con_va + 1; //不是4

printk("gpj2con_va=%pgpj2dat_va=%p\n", gpj2con_va,gpj2dat_va);

//8)创建class

gec210_led_class = class_create(THIS_MODULE, "led_class");

if(gec210_led_class == NULL)

{

printk("class create error\n");

ret = -EBUSY;

goto failed_class_create;

}

//9)创建device

gec210_led_device = device_create(gec210_led_class,NULL,

                  led_drv_num,NULL,"led_drv"); // /dev/led_drv

if(gec210_led_device == NULL)

{

printk("class device error\n");

ret = -EBUSY;

goto failed_device_create;

}

//led1~4,初始状态是灭的

*gpj2con_va &= ~0xffff;

*gpj2con_va |= 0x1111;

 

*gpj2dat_va |= 0xf;

return 0;

failed_device_create:

class_destroy(gec210_led_class);

failed_class_create:

iounmap(gpj2con_va);

failed_ioremap:

release_mem_region(0xe0200280,8);

failed_request_mem_region:

cdev_del(&led_drv);

failed_cdev_add:

unregister_chrdev_region(led_drv_num,  1);

return ret;

}

 

static void __exit gec210_led_exit(void) //驱动卸载函数

{

unregister_chrdev_region(led_drv_num,  1);

cdev_del(&led_drv);

release_mem_region(0xe0200280,8);

iounmap(gpj2con_va);

device_destroy(gec210_led_class,led_drv_num);

class_destroy(gec210_led_class);

printk("good bye gec210\n");

}

 

module_init(gec210_led_init); //驱动的入口

module_exit(gec210_led_exit); //驱动的出口

 

//内核模块的描述

MODULE_AUTHOR("bobeyfeng@163.com");

MODULE_DESCRIPTION("the first demo of module");

MODULE_LICENSE("GPL"); //符合GPL协议

MODULE_VERSION("V1.0");

//-----------------------------------

2. filename: test.c

 

#include <stdio.h>

#include <fcntl.h>

int main(void)

{

int fd;

int ret;

char buf[2];

//"/dev/led_drv" ---linux驱动的设备文件节点(node

fd = open("/dev/led_drv", O_WRONLY);

if(fd <0)

{

perror("open led_drv:");

return -1;

}

while(1)

{

buf[1] = 1;buf[0]=3; //led3 on

ret = write(fd,buf,sizeof(buf));

if(ret < 0)

{

perror("write led_drv: ");

return -1;

}

sleep(1);

buf[1] = 0;buf[0]=3; //led3 on

ret = write(fd,buf,sizeof(buf));

if(ret < 0)

{

perror("write led_drv: ");

return -1;

}

sleep(1);

}

close(fd);

return 0;

}

//------------------------------------

3. filename: Makefile

 

obj-m += led_drv.o

#KERNELDIR := /lib/modules/$(shell uname -r)/build

KERNELDIR := /home/gec/linux-2.6.35.7-gec-v3.0-gt110

PWD:=$(shell pwd)

 

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:

rm -rf *.o *.mod.c *.mod.o *.ko

 

//------------------------------------

A代码二

1. filename: led_drv.c

  /*LED1 --- GPJ2_0  --- S5PV210_GPJ2(0)

LED2 --- GPJ2_1  --- S5PV210_GPJ2(1)

LED3 --- GPJ2_2  --- S5PV210_GPJ2(2)

LED4 --- GPJ2_3  --- S5PV210_GPJ2(3)*/

 

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/uaccess.h>

#include <linux/ioport.h>

#include <linux/io.h>

#include <linux/device.h>

#include <linux/gpio.h> //GPIO标准接口函数

 

//1)定义一个字符设备cdev

static struct cdev led_drv;

 

static unsigned int led_major = 0; //0-->动态分配,>0-->静态注册

static unsigned int led_minor = 0;

static dev_t led_drv_num;

 

static struct class *gec210_led_class;

static struct device *gec210_led_device;

 

struct led_gpio{

unsigned int gpio_num;

char gpio_name[12];

};

 

static struct led_gpio gec210_leds[4] = {

{

.gpio_num = S5PV210_GPJ2(0),

.gpio_name = "GPJ2_0-LED1",

},

{

.gpio_num = S5PV210_GPJ2(1),

.gpio_name = "GPJ2_1-LED2",

},

{

.gpio_num = S5PV210_GPJ2(2),

.gpio_name = "GPJ2_2-LED2",

},

{

.gpio_num = S5PV210_GPJ2(3),

.gpio_name = "GPJ2_3-LED4",

},

};

 

//3)定义文件操作集,并初始化

//char buf[2],buf[1]灯的状态:1--on0-->off

//            buf[0]哪一个led1/2/3/4

ssize_t gec210_led_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)

{

int ret;

char kbuf[2];

if(len != 2)

return -EINVAL;

ret = copy_from_user(kbuf,buf,len); //从用户空间拷贝数据

if(ret!= 0)

return -EFAULT;

if( (kbuf[0]<1) || (kbuf[0]>4) )

return -EINVAL;

if(kbuf[1]==1)

gpio_set_value(gec210_leds[kbuf[0]-1].gpio_num, 0);

else if(kbuf[1]==0)

gpio_set_value(gec210_leds[kbuf[0]-1].gpio_num, 1);

else

return -EINVAL;

return len;

}

 

static struct file_operations gec210_led_fops = {

.owner = THIS_MODULE,

.write = gec210_led_write,

};

 

static int __init gec210_led_init(void) //驱动的初始化及安装函数

{

int ret;

int i;

//2)申请/注册设备号

if(led_major == 0){

ret = alloc_chrdev_region(&led_drv_num, led_minor, 1, "gec210_leds");

}

else{

led_drv_num = MKDEV(led_major,led_minor);

ret = register_chrdev_region(led_drv_num,  1, "gec210_leds");

}

if(ret < 0){

printk("led_drv_num is error \n");

return ret;

}

 

//4)初始化cdev

cdev_init(&led_drv,  &gec210_led_fops);

 

//5)cdev加入kernel

ret = cdev_add(&led_drv,led_drv_num, 1 );

if(ret < 0){

printk("cdev add error\n");

goto failed_cdev_add;

}

 

//6)创建class

gec210_led_class = class_create(THIS_MODULE, "led_class");

if(gec210_led_class == NULL)

{

printk("class create error\n");

ret = -EBUSY;

goto failed_class_create;

}

//7)创建device

gec210_led_device = device_create(gec210_led_class,NULL,

                  led_drv_num,NULL,"led_drv"); // /dev/led_drv

if(gec210_led_device == NULL)

{

printk("class device error\n");

ret = -EBUSY;

goto failed_device_create;

}

for(i=0;i<4;i++)

{

ret = gpio_request(gec210_leds[i].gpio_num, gec210_leds[i].gpio_name);

if(ret < 0)

{

printk("gpio request error %s\n", gec210_leds[i].gpio_name);

goto failed_gpio_request;

}

gpio_direction_output(gec210_leds[i].gpio_num,0x1);

}

 

return 0;

failed_gpio_request:

while(i--)//--i

gpio_free(gec210_leds[i].gpio_num);

device_destroy(gec210_led_class,led_drv_num);

failed_device_create:

class_destroy(gec210_led_class);

failed_class_create:

cdev_del(&led_drv);

failed_cdev_add:

unregister_chrdev_region(led_drv_num,  1);

return ret;

}

 

static void __exit gec210_led_exit(void) //驱动卸载函数

{

int i;

unregister_chrdev_region(led_drv_num,  1);

cdev_del(&led_drv);

 

device_destroy(gec210_led_class,led_drv_num);

class_destroy(gec210_led_class);

for(i=0;i<4;i++)

gpio_free(gec210_leds[i].gpio_num);

printk("good bye gec210\n");

}

 

module_init(gec210_led_init); //驱动的入口

module_exit(gec210_led_exit); //驱动的出口

 

//内核模块的描述

MODULE_AUTHOR("bobeyfeng@163.com");

MODULE_DESCRIPTION("the first demo of module");

MODULE_LICENSE("GPL"); //符合GPL协议

MODULE_VERSION("V1.0");

//-------------------------------------------------

2. Filename: test.c

#include <stdio.h>

#include <fcntl.h>

int main(void)

{

int fd;

int ret;

char buf[2];

//"/dev/led_drv" ---linux驱动的设备文件节点(node

fd = open("/dev/led_drv", O_WRONLY);

if(fd <0)

{

perror("open led_drv:");

return -1;

}

while(1)

{

buf[1] = 1;buf[0]=3; //led3 on

ret = write(fd,buf,sizeof(buf));

if(ret < 0)

{

perror("write led_drv: ");

return -1;

}

sleep(1);

buf[1] = 0;buf[0]=3; //led3 on

ret = write(fd,buf,sizeof(buf));

if(ret < 0)

{

perror("write led_drv: ");

return -1;

}

sleep(1);

}

close(fd);

return 0;

}

//--------------------------------------------------

3. Filename: Makefile

 

obj-m += led_drv.o

#KERNELDIR := /lib/modules/$(shell uname -r)/build

KERNELDIR := /home/gec/linux-2.6.35.7-gec-v3.0-gt110

PWD:=$(shell pwd)

 

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:

rm -rf *.o *.mod.c *.mod.o *.ko 

0 0
原创粉丝点击