linux 驱动笔记(五)

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

第七章 驱动编译进内核

1 将驱动编译进内核的步骤

将驱动编译成了一个*.ko,独立于zImage的,是一个独立的module,驱动使用的时候就可以安装,不用的时候可以卸载。

将驱动编译进zImage,在zImage启动的过程中,自动的安装该驱动。

 

1.1 将驱动的原文件,放到内核源码包中去

drivers/char创建一个目录led

led_drv.c拷贝到drivers/char/led/

 

 

1.2 drivers/char/led/下创建Kconfig

填写如下内容:

menu "leds driver for GEC210"

config GEC210_LEDS

tristate "GEC210 leds driver"

default y

depends on CPU_S5PV210

help

  this is a driver for LEDs on GEC210

endmenu

 

分析:

1)菜单有名称,menu开始,endmenu结束

2config GEC210_LEDS

定义一个条件编译选项GEC210_LEDS。我们在做make menuconfig,对条件编译选项进行赋值(Y M N),

赋完值后的条件编译选项存放在.config中。条件编译选项给Makefile来使用,确定驱动源码如何编译。

 

3tristate "GEC210 leds driver"

条件编译选项的值有三种选择:Y M N

4)default y

条件编译选项的默认值

5depends on CPU_S5PV210

条件编译选项的依赖,只有选中的依赖(CPU_S5PV210=y),才有可能对这个条件编译选项进行赋值。

1.3 drivers/char/led/下创建Makefile

makefile是根据条件编译选项的值,对驱动源码如何进行处理。

填写如下内容:

obj-$(CONFIG_GEC210_LEDS) += led_drv.o

1.4 修改上一级目录的Kconfig

drivrs/char/Kconfig中加入:

source "drivers/char/led/Kconfig"

1.5 修改上一级目录的Makefile

drivrs/char/Makefile中加入:

obj-$(CONFIG_GEC210_LEDS)  += led/

1.6 make menuconfig

 

Device Drivers  --->

Character devices  --->  

leds driver for GEC210  --->

<*> GEC210 leds driver (NEW)  

1.7 编译

#make

CC      drivers/char/led/led_drv.o

OBJCOPY arch/arm/boot/zImage

Kernel: arch/arm/boot/zImage is ready

1.8 下载zImageGEC210,并启动zImage

zImage的启动输出:

[    1.042413] s3cfb s3cfb: registered successfully

[    1.243565] leds driver for GEC210

[    1.247087] PA FB = 0x45FAF000, bits per pixel = 32

1.9 查看设备文件

[root@GEC210 /]# ls /dev/led_drv -l

crw-rw----    1 root     root      253,   0 Jan  1 13:35 /dev/led_drv

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

 

A代码二 蜂鸣器控制

1. Filename: buzzer_drv.test.c

 

#include <stdio.h>

#include <error.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>

#include <sys/stat.h>

#include <sys/types.h>

 

int main (int argc,char **argv)

{

int fd ,ret,err;

fd = open ("/dev/buzzer_drv",O_RDWR);

if (fd == -1)

{

perror ("open failed");

return -1;

}

while (1)

{

printf ("please enter:\n");

scanf  ("%d",&ret);

getchar ();

err = write (fd,&ret,sizeof (int));

if (err == -1)

{

perror ("write failed");

return -1;

}

printf ("wirte : %d byte\n",err);

printf ("write : %d\n",ret);

}

close (fd);

return 0;

}

 

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

2. Filename: char_drv.c

#include <linux/io.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/ioport.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/uaccess.h>

 

static struct cdev buzzer_drv; //定义字符设备

 

static unsigned int buzzer_major = 200; //主设备号若是0,是动态申请;若不是,则默认是静态注册

static unsigned int buzzer_minor = 0;   //次设备号

 

static dev_t buzzer_drv_num; //设备号

static struct class  *class;     //

static struct device *device;           //设备名称

 

static struct resource *buzzer_res;     //申请物理内存

static unsigned long *GPD0CON;          //存放物理内存映射后的虚拟地址

static unsigned long *GPD0DAT;

 

static int buzzer_open (struct inode *inode,struct file *filp)

{

printk ("ready to open\n");

*GPD0CON &= ~(0xf<<0);

*GPD0CON |=  (0x1<<0); //输出状态

*GPD0DAT &= ~(0xf<<0); //高电平,蜂鸣器不响

return 0;

}

 

static int buzzer_write (struct file *filp,const char __user *buf,size_t len,loff_t *ppos)

{

int err,ret;

printk ("ready to write\n");

err = copy_from_user(&ret,buf,len);

if (err != 0)

{

printk ("copy from user failed\n");

return -EFAULT;

}

printk ("%d\n",ret);

/*根据用户程序决定蜂鸣器情况*/

*GPD0DAT ^= (ret<<0);

return 0;

}

 

static int buzzer_release (struct inode *inode, struct file *filp)

{

printk ("release success\n");

return 0;

}

 

/*定义文件操作集*/

static struct file_operations buzzer_drv_fops = {

.owner = THIS_MODULE,

.open  = buzzer_open,

.write = buzzer_write,

.release = buzzer_release,

};

 

/*驱动初始化及安装函数*/

static int __init buzzer_init (void)

{

int err;

buzzer_drv_num = MKDEV (buzzer_major,buzzer_minor);//驱动设备的设备号

/*注册设备*/

err = register_chrdev_region (buzzer_drv_num,1,"buzzer_drv");

if (err < 0)

{

printk ("get buzzer_drv_num failed\n");

return err;

}

/*初始化字符设备*/

cdev_init (&buzzer_drv,&buzzer_drv_fops);

/*字符设备添加至内核*/

err = cdev_add (&buzzer_drv,buzzer_drv_num,1);

if (err < 0)

{

printk ("add failed\n");

goto add_failed;

}

/*自动创建设备文件*/

/*注册一个类*/

class = class_create (THIS_MODULE,"buzzer_drv");

if (IS_ERR(class))

{

printk ("create class failed\n");

goto add_failed;

}

/*从属的类下,在/dev目录下创建设备节点*/

device = device_create (class,NULL,buzzer_drv_num,NULL,"buzzer_drv");

if (IS_ERR(device))

{

printk ("create device failed\n");

goto device_failed;

}

printk ("init success\n");

/*led灯初始化*/

/*申物理内存区域*/

buzzer_res = request_mem_region(0xe02000A0,8,"buzzer_drv");

if (buzzer_res == NULL)

{

printk ("request memory failed\n");

goto request_failed;

}

/*映射虚拟内存*/

GPD0CON =(unsigned long *) ioremap (0xe02000A0,8);

if (GPD0CON == NULL)

{

printk ("ioremap failed\n");

goto ioremap_failed;

//return -EFAULT;

}

GPD0DAT = GPD0CON + 0x1;

printk ("buzzer init success\n");

return 0;

ioremap_failed:

/*释放物理内存空间*/

release_mem_region (0xe02000A0,8);

request_failed:

/*销毁设备节点*/

device_destroy (class,buzzer_drv_num);

device_failed:

/*销毁类*/

class_destroy(class);

add_failed:

/*删除字符设备*/

    cdev_del (&buzzer_drv);

/*注销设备号*/

unregister_chrdev_region (buzzer_drv_num,1);

return -1;

}

 

/*驱动卸载*/

static void __exit buzzer_exit (void)

{

/*取消IO内存映射*/

iounmap (GPD0CON);

/*释放物理内存空间*/

release_mem_region (0xe02000A0,8);

/*销毁设备节点*/

device_destroy (class,buzzer_drv_num);

    /*清除类*/

class_destroy  (class);

/*删除字符设备*/

    cdev_del    (&buzzer_drv);

/*注销设备号*/

unregister_chrdev_region (buzzer_drv_num,1);

printk ("exit success\n");

}

 

/*驱动入口*/

module_init (buzzer_init);

/*驱动出口*/

module_exit (buzzer_exit);

 

/*内核模块描述,使用#modinfo查看相应信息*/

MODULE_AUTHOR  ("yuan");

MODULE_LICENSE ("GPL");

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

3. Filename: Makefile

 

obj-m += char_drv.o

 

KERNELDIR := /opt/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

 

 

 

 

 

 

 

 

 

 

第八章 IOCTL的使用

1 IOCTL的使用举例

1.1 应用程序访问显卡

 

#include <linux/fb.h>

 

struct fb_fix_screeninfo finfo;

struct fb_fix_screeninfo finfo;

 

fd = open ("/dev/fb0",O_RDWR);

 

//FBIOGET_FSCREENINFO-->读取显卡中的液晶屏的固定信息(命令)

//&finfo ---读到的信息放的位置

ioctl(fd,FBIOGET_FSCREENINFO,&finfo)

printf("The mem is :%d\r\n",finfo.smem_len);  

printf("The line_length is :%d\r\n",finfo.line_length);

 

//fb驱动发送命令FBIOGET_VSCREENINFO,读取液晶屏的可变信息

ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)

printf("The xres is :%d\r\n",vinfo.xres);  

printf("The yres is :%d\r\n",vinfo.yres);  

printf("bits_per_pixel is :%d\r\n",vinfo.bits_per_pixel);

 

总结:应用程序可以通过ioctl向驱动程序发送命令,然后再从驱动程序中读取数据。

 

1.2 ADC的驱动

 

#define ADC_INPUT_PIN _IOW('S', 0x0c, unsigned long)

 

static const struct file_operations s3c_adc_fops = {

.owner = THIS_MODULE,

.read = s3c_adc_read,

.open = s3c_adc_open,

.ioctl = s3c_adc_ioctl,

};

 

static int s3c_adc_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

{

 

switch (cmd) {

case ADC_INPUT_PIN: //应用程序发下来的命令

adc_port = (unsigned int) arg;      //驱动程序得到应用程序写来的argADC的转换通道

return 0;

 

default:

return -ENOIOCTLCMD;

}

}   

 

1.3对应这个驱动程序的应用程序:

#include <sys/ioctl.h>

#define ADC_INPUT_PIN _IOW('S', 0x0c, unsigned long)

 

fd = open("/dev/adc",O_RDWR)

ioctl(fd,ADC_INPUT_PIN, 0)

 

read(fd,  , )  ---->分析adc驱动的read函数

 

close(fd)

 

2 用户空间与内核空间的ioctl函数

 

用户空间ioctl() ----->系统调用----->内核空间ioctl()

 

2.1 用户空间的ioctl()

 #include <sys/ioctl.h>

原型

int ioctl(int fd, int cmd);

int ioctl(int fd, int cmd, unsigned long arg);

参数说明:

int fd ---> 文件描述符

int cmd ---> 应用程序向驱动程序发送的命令

unsigned int arg ---> 应用程序与驱动程序之间交互的参数(双向的)

返回值:

成功,返回0

失败,返回负数的错误码

 

2.2 内核空间的ioctl()

原型

int ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)

long unlocked_ioctl(struct file *file, unsigned int cmd,unsigned long arg)

参数说明:

unsigned int cmd --->应用程序传下来的命令

unsigned long arg --->应用程序与驱动程序之间交互的数据

区别:

linux内核的版本有关,早期版本file_operations只有ioctl(),中间过渡版本包含了ioctl()unlocked_ioctl(),

2.6.36以后的版本,支持支unlocked_ioctl()。取消了内核中的一个叫内核锁。在驱动设计过程中,这个函数是没有区别的。

3 ioctl命令

#define ADC_INPUT_PIN _IOW('S', 0x0c, unsigned long)

IOCTL的命令是由函数生成的,并不是随便选的值1/2/3/4/5/6......

提示:

    应用程序向驱动程序发送命令cmd=2cmd就传不到驱动程序。中间被系统调用过滤掉了。我们应该使用cmd的标准定义方法。

 

生成命令的函数:

_IO(type,nr)    --->应用程序向驱动程序发送命令,不发送参数

_IOR(type,nr,size) --->应用程序向驱动程序发送命令,从驱动程序中获取参数

_IOW(type,nr,size) --->应用程序向驱动程序发送命令,向驱动程序中写入参数

 

参数说明:

type ---> 命令的类型(魔数),相当于一个秘钥,该秘钥一般是一个ASCII码值。

nr    ---> 序号,该魔数下,命令的需要,是一个整型值。0 1 2 3 4

size ---> 传递参数的大小,函数中写的是参数的类型

/***************

魔数和魔字符串 --->就是指在代码中出现但没有解释的数字常量字符串,魔数是稳定核素"幻数"的另称

很多类型的文件,其起始的几个字节的内容是固定的(或是有意填充,或是本就如此)。因此这几个字节的内容也被称为魔数 (magic number),因为根据这几个字节的内容就可以确定文件类型。

如果在某个程序中你使用了魔数,那么在几个月(或几年)后你将很可能不知道它的含义是什么。

在一个Project里面,避免使用魔数(Magic Number)和魔字符串(Magic String)是相当必要的。通过定义的常量access特定的字符串和数字也已经是软件开发的standard

****************/

命令的组成:方向(2bits),魔数(8bits) 序号(8bits) 参数大小(14bits

例:

#define GEC210_LED_ON _IOW('L', 0x01, unsigned long)

#define GEC210_LED_OFF _IOW('L', 0x02, unsigned long)

 

A作业

1.使用ioctl实现蜂鸣器的驱动

 

2.使用read实现按键的驱动

 

3.使用ioctl实现按键的驱动

/drivers/char/watchdog/s3c2410_wdt.c

 

B 代码一

 

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标准接口函数

#include <linux/ioctl.h>

 

#define GEC210_LED_ON _IOW('L', 0x01, unsigned long)

#define GEC210_LED_OFF _IOW('L', 0x02, unsigned long)

 

//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)定义文件操作集,并初始化

//     cmd是灯的状态,arg哪一个led1/2/3/4

static long gec210_led_ioctl(struct file *file, unsigned int cmd,unsigned long arg)

{

switch (cmd) {

case GEC210_LED_ON:

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

return 0;

case GEC210_LED_OFF:

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

return 0;

default:

return -ENOIOCTLCMD;

}

}

 

static struct file_operations gec210_led_fops = {

.owner = THIS_MODULE,

.unlocked_ioctl = gec210_led_ioctl,

};

 

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

#include <stdio.h>

#include <fcntl.h>

#include <sys/ioctl.h>

 

#define GEC210_LED_ON _IOW('L', 0x01, unsigned long)

#define GEC210_LED_OFF _IOW('L', 0x02, unsigned long)

 

 

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)

{

ret = ioctl(fd,GEC210_LED_ON,4);

if(ret < 0)

{

perror("ioctl led_drv: ");

return -1;

}

sleep(1);

ret = ioctl(fd,GEC210_LED_OFF,4);

if(ret < 0)

{

perror("ioctl 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

 

 

 

 

第九章 混杂设备

1什么是混杂设备

混杂设备(miscdevice)又叫做杂项设备,是普通的字符设备(cdev)的封装,用来降低驱动的设计难度,使一个驱动的设计过程更加简单。

本意:有些驱动,又像块设备,又像字符设备,可以用混杂设备来设计。混杂设备也是一个字符设备,但是对字符设备进行了封装。

 

主设备号是10.

crw-rw----    1 root     root       10, 131 Jan  1 15:54 adc

crw-rw----    1 root     root       10,  52 Jan  1 15:54 humidity

crw-rw----    1 root     root       10, 254 Jan  1 15:54 s3c-jpg

crw-rw----    1 root     root       10, 252 Jan  1 15:54 s3c-mfc

 

# cat /proc/misc

 52 humidity

 53 timer_irq

 54 pwm

131 adc

 55 network_throughput

 56 network_latency

 57 cpu_dma_latency

 58 device-mapper

242 CEC

243 HPD

240 sec-g2d

254 s3c-jpg

252 s3c-mfc

 59 alarm

  1 psaux

 60 mtp_usb

 61 android_adb_enable

 62 android_adb

134 apm_bios

 63 ashmem

 

# cat /proc/devices

Character devices:

10 misc

 

使用misc device的时候,不需要创建classdevice,可以自动生成设备文件。

class已经创建好的 ---> /sys/class/misc/

device --->创建混杂设备的时候,就会创建一个device

 

2 混杂设备的使用

 

#include <linux/miscdevice.h>

2.1 定义混杂设备

static struct miscdevice s3c_adc_miscdev = {

.minor = ADC_MINOR, //混杂设备的次设备号,系统自动分配:MISC_DYNAMIC_MINOR

.name = "adc",  //misc device的名字,也是设备文件的名字

.fops = &s3c_adc_fops,  //文件操作集

};

 

2.2 将混杂设备注册到内核

//A zero is returned on success and a negative errno code for failure

int misc_register(struct miscdevice * misc)

 

2.3 注销一个混杂设备

int misc_deregister(struct miscdevice *misc)

 

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标准接口函数

#include <linux/ioctl.h>

#include <linux/miscdevice.h>

 

#define GEC210_LED_ON _IOW('L', 0x01, unsigned long)

#define GEC210_LED_OFF _IOW('L', 0x02, unsigned long)

 

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)定义文件操作集,并初始化

//     cmd是灯的状态,arg哪一个led1/2/3/4

static long gec210_led_ioctl(struct file *file, unsigned int cmd,unsigned long arg)

{

switch (cmd) {

case GEC210_LED_ON:

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

return 0;

case GEC210_LED_OFF:

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

return 0;

default:

return -ENOIOCTLCMD;

}

}

 

static struct file_operations gec210_led_fops = {

.owner = THIS_MODULE,

.unlocked_ioctl = gec210_led_ioctl,

};

 

static struct miscdevice s3c_adc_miscdev = {

.minor = MISC_DYNAMIC_MINOR,

.name = "led_drv",  //misc device的名字,也是设备文件的名字

.fops = &gec210_led_fops,  //文件操作集

};

 

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

{

int ret;

int i;

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);

}

ret = misc_register(&s3c_adc_miscdev);

if(ret < 0)

{

printk("misc register error\n");

goto failed_misc_register;

}

 

return 0;

failed_misc_register:

failed_gpio_request:

while(i--)//--i

gpio_free(gec210_leds[i].gpio_num);

return ret;

}

 

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

{

int i;

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

gpio_free(gec210_leds[i].gpio_num);

misc_deregister(&s3c_adc_miscdev);

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>

#include <sys/ioctl.h>

 

#define GEC210_LED_ON _IOW('L', 0x01, unsigned long)

#define GEC210_LED_OFF _IOW('L', 0x02, unsigned long)

 

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)

{

ret = ioctl(fd,GEC210_LED_ON,4);

if(ret < 0)

{

perror("ioctl led_drv: ");

return -1;

}

sleep(1);

ret = ioctl(fd,GEC210_LED_OFF,4);

if(ret < 0)

{

perror("ioctl 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

//蜂鸣器驱动

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

 

#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标准接口函数

#include <linux/ioctl.h>

#include <linux/miscdevice.h>

 

#define GEC210_BEEP_ON _IO('L', 0x01)

#define GEC210_BEEP_OFF _IO('L', 0x02)

 

#define BEEP_GPIO S5PV210_GPD0(0)  //

static const char beep_name[]="gpd0_0_beep";

 

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

//cmd是灯的状态,arg哪一个led1/2/3/4

static long gec210_beep_ioctl(struct file *file, unsigned int cmd,unsigned long arg)

{

switch (cmd) {

case GEC210_BEEP_ON:

gpio_set_value(BEEP_GPIO, 1);

return 0;

case GEC210_BEEP_OFF:

gpio_set_value(BEEP_GPIO, 0);

return 0;

default:

return -ENOIOCTLCMD;

}

}

 

static struct file_operations gec210_beep_fops = {     //结构体

.owner = THIS_MODULE,

.unlocked_ioctl = gec210_beep_ioctl,

};

 

static struct miscdevice s3c_beep_miscdev = {          //结构体

.minor = MISC_DYNAMIC_MINOR,

.name = "beep_drv",  //misc device的名字,也是设备文件的名字

.fops = &gec210_beep_fops,  //文件操作集

};

 

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

{

int ret;

ret = gpio_request(BEEP_GPIO, beep_name);   

if(ret < 0)

{

printk("gpio request error %s\n", beep_name);

goto failed_gpio_request;

}

gpio_direction_output(BEEP_GPIO,0x0);  //初始为关,即加载驱动时为不响

ret = misc_register(&s3c_beep_miscdev); //注册

if(ret < 0)

{

printk("misc register error\n");

goto failed_misc_register;

}

 

return 0;

 

failed_misc_register:

failed_gpio_request:

gpio_free(BEEP_GPIO);

return ret;

}

 

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

{

gpio_free(BEEP_GPIO);

misc_deregister(&s3c_beep_miscdev);

printk("good bye gec210\n");

}

 

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

module_exit(gec210_beep_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>

#include <sys/ioctl.h>

 

#define GEC210_BEEP_ON _IO('L', 0x01)  //

#define GEC210_BEEP_OFF _IO('L', 0x02)  //

 

 

int main(void)

{

int fd;

int ret;

char buf[2];

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

fd = open("/dev/beep_drv", O_WRONLY);  //打开

if(fd <0)

{

perror("open led_drv:");

return -1;

}

while(1)

{

ret = ioctl(fd, GEC210_BEEP_ON);//开,可有两个参数,也可有三个参数

if(ret < 0)

{

perror("ioctl led_drv: ");

return -1;

}

sleep(1);//1秒,即响一秒

ret = ioctl(fd, GEC210_BEEP_OFF);//关,

if(ret < 0)

{

perror("ioctl led_drv: ");

return -1;

}

sleep(3); //间隔,停3秒,

}

close(fd); //关闭

return 0;

}

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

3. Filename: Makefile

 

obj-m += beep_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

 

 

第十章 linux中的内存分配

1 MMU 内存管理单元

1.1  什么是MMU

MMU --- memory management unit  内存管理单元

MMU --- ARM内核中的一个硬件模块,不是一个软件协议包。

 

1.2  MMU的作用

1.2.1将虚拟地址转换成物理地址

1.2.2设置地址的访问属性:只读  可读写  禁止访问的。

一般比较复杂的操作系统,运行的时候都需要使用MMU:嵌入式linux  wince  vxworks  .....

不需要MMUuclinux  uC/OS-II (实时性较好)

 

1.3  MMU将虚拟地址转换成物理地址的单位

映射的单位:

1.3.1 section1MB    --->GEC210uboot

1.3.2大页 large page 64KB

1.3.3小页 small page 4KB --->linux内核 (page

1.3.4极小页 tiny page 1kB

 

1.4  MMU如何做地址转换(以“段”来举例)

MMU将虚拟地址转换成物理地址的中心思想-----查表。描述一个MMU的表主要包含两个内容,分别是:

1.4.1表的索引 --->虚拟地址

1.4.2表的内容 --->物理地址,和该地址区的访问属性。

 

分析:物理地址空间(虚拟地址空间)大小是4GB,段的大小是1MB,所以表的索引值有4k个。

1MB为单位进行地址映射,最多需要4KB页表项(条目,entry)。

 

表的索引值是虚拟地址的高12bits,即VA[31:20]

页表的内容是物理地址的高12bitsPA[31:20]和访问属性

 

问:

MMU如果通过页表来进行虚拟地址转换成物理地址的呢???

答:

CPU使用一个32bits的虚拟地址VA[31:0],改虚拟地址送给MMUMMU使用VA[31:20]来查找页表,找到页表中对应的条目,并且读出条目的内容,该内容是:PA[31:20]和访问属性。PA[19:0] = VA[19:0].

 

1.5  页表

1.5.1页表是在开启MMU之前初始化好的,是我们做初始化的。

1.5.2页表的索引是虚拟地址,页表的内容是物理地址和访问属性。所以虚拟地址和物理地址的关系是我们定的。

1.5.3 MMU查表的过程是一个硬件自动查表的过程,用户不需要干预。

1.5.4页表要放在DDR2内存中,应为我们可以在系统运行过程中,改写这个页表

1.5.5虚拟地址 = ioremap(物理地址)  --->改写MMU页表的过程,重新确定虚拟地址和物理地址之间的关系。

   ioremap() --->io内存的重新映射。

 

补充资料:S3C2410的使用手册--附录3

         嵌入式linux应用开发完全手册--->7章 内存管理单元MMU

 

2 内存的分配

2.1  用户空间内存分配方法

#include <stdlib.h>

 

void *malloc(size_t size);

void free(void *ptr);

 

2.2  内核空间的分配内存方法

2.2.1 kmalloc

原型

#include <linux/slab.h>

void *kmalloc(size_t size, gfp_t flags)

参数说明:

size_t size --->分配内存的大小(字节数),不能超过128KB

gfp_t flags   --->分配内存的标志,常用的分配标志

    GFP_ATOMIC --->分配内存的过程是一个“原子过程”,该过程不能被打断。

GFP_DMA   --->DMA控制器分配内存,使用该标志,确保分配的物理地址和虚拟地址都是是连续的。

GFP_KERNEL --->内存的正常分配。

 

void kfree(const void *objp)

 

kmalloc()的变形:

void *kzalloc(size_t size, gfp_t flags)

void kfree(const void *objp)

 

2.2.2 vmalloc

原型

void *vmalloc(unsigned long size)

void vfree(const void *addr)

 

linux内核的启动输出:

[    0.000000] Virtual kernel memory layout:

[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)--->中断向量表

[    0.000000]     fixmap  : 0xfff00000 - 0xfffe0000    ( 896 kB) --->固定映射区

[    0.000000]     DMA     : 0xff000000 - 0xffe00000   (  14 MB) --->DMA内存区  GFP_DMA

[    0.000000]     vmalloc : 0xe0800000 - 0xfc000000    ( 440 MB) --->vmalloc分配的内存区

[    0.000000]     lowmem  : 0xc0000000 - 0xe0000000   ( 512 MB) --->正常分配的内存区  GFP_KERNEL

[    0.000000]     modules : 0xbf000000 - 0xc0000000   (  16 MB) --->安装的ko

[    0.000000]     .init : 0xc0008000 - 0xc008e000   ( 536 kB)

[    0.000000]     .text : 0xc008e000 - 0xc07d5000 (7452 kB)

[    0.000000]     .data : 0xc07d6000 - 0xc08345a0      ( 378 kB)

 

2.3  kmallocvmalloc的区别

2.3.1分配内存的位置不一样

 kmalloclowmem区,而vmallocvmalloc区。

2.3.2分配内存的大小不同

 kmalloc不能超过128kB,vmalloc没有限制

2.3.3如果给DMA控制器来分配内存,只能使用kmalloc(, GFP_DMA)

2.3.4分配内存的过程是一个原子过程,只能使用kmalloc(,GFP_ATOMIC)

2.3.5 kmalloc可以保证分配内存的物理地址和虚拟地址都是连续的,但是vmalloc不能保证物理地址是连续的。

第十一章 linux中的中断

1中断的使用

#include <linux/interrupt.h>

1.1  申请中断

原型

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)

参数说明:

unsigned int irq --->中断号,每个中断源都有一个中断号

irq_handler_t handler --->中断源对应的中断服务程序

unsigned long flags --->中断的标志

const char *name --->自定义的中断名称

void *dev --->向中端服务程序传递的参数

返回值:

成功,返回0

失败,返回负数的错误码

 

1.2  中断服务程序

原型

  irqreturn_t (*irq_handler_t)(int irq, void *dev)

{

return IRQ_HANDLED;

}

参数说明:

int irq --- 响应该中断服务程序的中断号

void *dev ---注册中断的时候,使用的一个参数

 

1.3  释放中断

原型

void free_irq(unsigned int irq, void *dev_id)

参数说明:

int irq  --- 注册该中断服务程序的中断号

void *dev  ---注册中断的时候,使用的一个参数

 

1.4  中断号

每个中断源都有一个中断号,因为中断源是与硬件相关的,所以中断号应该是在具体的硬件平台(具体的CPU)中定义。

 

1) UART的中断号

#define IRQ_S5P_UART_RX0 (IRQ_S5P_UART_BASE0 + UART_IRQ_RXD)

#define IRQ_S5P_UART_TX0 (IRQ_S5P_UART_BASE0 + UART_IRQ_TXD)

#define IRQ_S5P_UART_ERR0 (IRQ_S5P_UART_BASE0 + UART_IRQ_ERR)

 

#define IRQ_S5P_UART_RX1 (IRQ_S5P_UART_BASE1 + UART_IRQ_RXD)

#define IRQ_S5P_UART_TX1 (IRQ_S5P_UART_BASE1 + UART_IRQ_TXD)

#define IRQ_S5P_UART_ERR1 (IRQ_S5P_UART_BASE1 + UART_IRQ_ERR)

 

#define IRQ_S5P_UART_RX2 (IRQ_S5P_UART_BASE2 + UART_IRQ_RXD)

#define IRQ_S5P_UART_TX2 (IRQ_S5P_UART_BASE2 + UART_IRQ_TXD)

#define IRQ_S5P_UART_ERR2 (IRQ_S5P_UART_BASE2 + UART_IRQ_ERR)

 

#define IRQ_S5P_UART_RX3 (IRQ_S5P_UART_BASE3 + UART_IRQ_RXD)

#define IRQ_S5P_UART_TX3 (IRQ_S5P_UART_BASE3 + UART_IRQ_TXD)

#define IRQ_S5P_UART_ERR3 (IRQ_S5P_UART_BASE3 + UART_IRQ_ERR)

 

2) PWM定时器的中断号

#define IRQ_TIMER0 S5P_TIMER_IRQ(0)

#define IRQ_TIMER1 S5P_TIMER_IRQ(1)

#define IRQ_TIMER2 S5P_TIMER_IRQ(2)

#define IRQ_TIMER3 S5P_TIMER_IRQ(3)

#define IRQ_TIMER4 S5P_TIMER_IRQ(4)

 

3) 外部中断的中断号

#define IRQ_EINT(x) ((x) < 16 ? ((x) + S5P_EINT_BASE1) \

: ((x) - 16 + S5P_EINT_BASE2))

x=0~31

 

1.5  中断的标志

 相当于是中断的处理方法或者中断的触发方式

1) 外部中断

IRQF_TRIGGER_RISING

IRQF_TRIGGER_FALLING

IRQF_TRIGGER_HIGH

IRQF_TRIGGER_LOW

IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING

 

2) 内部中断

IRQF_DISABLED ---> 相应当前中断的时候,关闭其他中断的相应

IRQF_SHARED --->当一个中断源多次注册的时候,使用该关键字

IRQF_TIMER --->申请一个定时器中断

2 查看安装后的驱动

# cat /proc/interrupts

           CPU0

 16:       1291    s3c-uart  s5pv210-uart

 18:        509    s3c-uart  s5pv210-uart

 39:          0  s5p_vic_eint  eth0

 45:          1  s5p_vic_eint  hpd

 46:          0  s5p_vic_eint  ft5x0x_ts

 50:          0         VIC  s3c-pl330.0

 51:          0         VIC  s3c-pl330.1

 52:          0         VIC  s3c-pl330.2

 58:          0         VIC  System timer

 61:      23368         VIC  rtc-tick

 78:        141         VIC  s3c2440-i2c.0

 83:          1         VIC  s3c2440-i2c.2

 87:         26         VIC  ehci_hcd:usb1, ohci_hcd:usb2

 88:          1         VIC  s3c-udc

 90:          0         VIC  mmc0

 91:          0         VIC  mmc1

 92:         13         VIC  mmc2

 93:          0         VIC  s3c-csis

 97:       8181         VIC  s3cfb

101:          0         VIC  s3c-fimc0

102:          0         VIC  s3c-fimc1

103:          0         VIC  s3c-fimc2

104:          0         VIC  s3c-jpg

105:          0         VIC  s3c-g2d

107:          0         VIC  s5p-tvout

108:          0         VIC  s5p-tvout

109:          0         VIC  s3c2440-i2c.1

110:          0         VIC  s3c-mfc

111:          0         VIC  s5p-tvout

130:         13         VIC  mmc3

131:          0         VIC  s5p-cec

160:          0    s5p-eint  key2_eint16

 

中断号  中断发生的次数  中断控制器   中断的名称

 

3多个中断源可以共用一个中断服务程序

 

 irqreturn_t (*irq_handler_t)(int irq, void *dev)

{

 

//1  int irq --->来确定中断源

//2  void *dev --->来确定中断源

 

 

return IRQ_HANDLED;

}

 

4等待队列

  我们可以定义一个等待队列,并给这个等待队列设置一个等待的条件,让进程访问等待队列的时候,首先判断等待条件,

条件为真 --->直接越过等待队列,继续向下运行。

条件为假 --->进程就会产生阻塞,就会进入睡眠状态,睡眠在等待队列。当条件为真的时候,我们可以唤醒等待队列中睡眠的进程,让这个进程继续运行。

 

等待队列的使用方法

 

#include <linux/wait.h>

 

4.1定义一个等待队列头

4.1.1静态的定义及初始化

DECLARE_WAIT_QUEUE_HEAD(name)

 

:

DECLARE_WAIT_QUEUE_HEAD(gec210_key_wq)

 

4.1.2动态的定义及初始化

定义:

static wait_queue_head_t gec210_key_wq;

初始化:

  init_waitqueue_head(wait_queue_head_t *q)

 

例:

static wait_queue_head_t gec210_key_wq;

init_waitqueue_head(&gec210_key_wq);

 

4.2判断等待条件

wait_event(wait_queue_head_t q, int condition);

wait_event_interruptible(wait_queue_head_t q, int condition)

 

4.3唤醒等待队列中的进程

wake_up(wait_queue_head_t *q)  

wake_up_interruptible(wait_queue_head_t *q)

 

5等待队列程序调试

5.1  安装驱动

 

5.2  执行应用程序(后台)

./test&

 

5.3  top

 

Mem: 12208K used, 332692K free, 0K shrd, 0K buff, 4832K cached

CPU:  0.0% usr  0.0% sys  0.0% nic  100% idle  0.0% io  0.0% irq  0.0% sirq

Load average: 0.00 0.00 0.00 1/41 152

  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND

  152    67 root     R     2260  0.6   0  0.0 top

   67     1 root     S     2260  0.6   0  0.0 -/bin/sh

    1     0 root     S     2256  0.6   0  0.0 init

  151    67 root     S     1576  0.4   0  0.0 ./test

   52     2 root     SW       0  0.0   0  0.0 [yaffs-bg-1]

    5     2 root     SW       0  0.0   0  0.0 [events/0]

   12     2 root     SW       0  0.0   0  0.0 [khubd]

    2     0 root     SW       0  0.0   0  0.0 [kthreadd]

    3     2 root     SW       0  0.0   0  0.0 [ksoftirqd/0]

    4     2 root     SW       0  0.0   0  0.0 [watchdog/0]

    6     2 root     SW       0  0.0   0  0.0 [khelper]

    7     2 root     SW       0  0.0   0  0.0 [async/mgr]

    8     2 root     SW       0  0.0   0  0.0 [suspend]

    9     2 root     SW       0  0.0   0  0.0 [sync_supers]

   10     2 root     SW       0  0.0   0  0.0 [bdi-default]

   11     2 root     SW       0  0.0   0  0.0 [kblockd/0]

   13     2 root     SW       0  0.0   0  0.0 [kseriod]

   14     2 root     SW       0  0.0   0  0.0 [kmmcd]

   15     2 root     SW       0  0.0   0  0.0 [cfg80211]

   16     2 root     SW       0  0.0   0  0.0 [rpciod/0]

 

进程test的状态是S --- sleep

 

A作业一:

1  采用静态定义等待队列的方法,定义一个等待队列。

 

2  判断等待条件的时候:wait_event(wait_queue_head_t q, int condition);

 

3  唤醒等待队列的时候:wake_up(&gec210_key_wq);

 

中断看进程的状态:

 

4  总结等待队列的使用方法

A作业二:

 

1. 设计Key2~Key5四个按键的驱动,这个外部中断使用一个中断服务程序

 

2.

#define init_waitqueue_head(q) \

do { \

static struct lock_class_key __key; \

\

__init_waitqueue_head((q), &__key); \

} while (0)

 

 

 

 

改成1

 

#define init_waitqueue_head(q) \

{ \

static struct lock_class_key __key; \

\

__init_waitqueue_head((q), &__key); \

}

 

 

使用do {  }while(0),宏定义函数,有什么特点???

 

 

B代码一:

 

1. Filename: led_drv.c

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/interrupt.h>

 

irqreturn_t key2_eint16_isr(int irq, void *dev)

{

printk("key2 is pressing, irq = %d\n",irq);

return IRQ_HANDLED;

}

 

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

{

int ret;

ret = request_irq(IRQ_EINT(16), key2_eint16_isr, IRQF_TRIGGER_FALLING,

"key2_eint16", NULL);

if(ret < 0)

{

printk("request irq error\n");

return ret;

}

printk("hello gec210\n"); //替代printf()

return 0;

}

 

static void __exit gec210_key_exit(void)

{

free_irq(IRQ_EINT(16), NULL);

printk("good bye gec210\n");

}

 

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

module_exit(gec210_key_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

 

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

 

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

 

B代码二:

1. Filename: led_drv.c

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/uaccess.h>

#include <linux/interrupt.h>

#include <linux/ioport.h>

#include <linux/io.h>

#include <linux/device.h>

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

#include <linux/miscdevice.h>

 

 

int num[4]={2,3,4,5};

 

 

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",

},

};

 

irqreturn_t key_eint_isr(int irq , void *dev) //void *dev 来区分不同的中断源

{

static volatile int led_flag = 1;

led_flag = !led_flag;

printk("key[%d] is pressed,irq is %d!\n",*((int *)dev),irq);

gpio_set_value(gec210_leds[*((int *)dev)-2].gpio_num,led_flag);

 

//gpio_set_value(gec210_leds[*((int *)dev)-2].gpio_num,1);

return IRQ_HANDLED;

}

 

static struct file_operations gec210_led_fops = {

.owner = THIS_MODULE,

};

 

static struct miscdevice s3c_adc_miscdev = {

.minor = MISC_DYNAMIC_MINOR, //混杂设备的次设备号,系统自动分配:MISC_DYNAMIC_MINOR

.name = "led_drv",  //misc device的名字,也是设备文件的名字

.fops = &gec210_led_fops,  //文件操作集

};

 

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

{

int ret;

int i;

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

{

ret = request_irq(IRQ_EINT(i+16),key_eint_isr,IRQF_TRIGGER_FALLING,"key_eint",num+i);

if(ret < 0)

{

printk("failed to request irq!\n");

goto failed_request_irq;

}

}

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);

}

ret = misc_register(&s3c_adc_miscdev);

if(ret < 0)

{

printk("misc register error\n");

goto failed_misc_register;

}

printk("hello gec210\n"); //替代printf()

return 0;

failed_request_irq:

while(i--)

free_irq(IRQ_EINT(i+16),num+i);

failed_gpio_request:

failed_misc_register:

while(i--)

gpio_free(gec210_leds[i].gpio_num);

return ret;

}

 

static void __exit gec210_key_exit(void)

{

int i;

for(i=16;i<20;i++)

free_irq(IRQ_EINT(i),num-16+i);

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

gpio_free(gec210_leds[i].gpio_num);

misc_deregister(&s3c_adc_miscdev);

printk("good bye gec210\n");

}

 

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

module_exit(gec210_key_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>

 

 

unsigned int flag =1;

 

int main(void)

{

flag ^= 1;

printf("1. %d\n", flag);

flag = !1;

printf("2. %d\n", flag);

flag =~1;

printf("3. %u\n", flag);

 

 

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

 

 

B 代码三:

1. Filename: key_drv.c

 

//当有按键按下,我们就可以读按键的状态;没有按键按下,进程就睡眠在驱动的read函数中。#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/gpio.h>

#include <linux/fs.h>

#include <linux/io.h>

#include <linux/ioport.h>

#include <linux/cdev.h>

#include <linux/uaccess.h>

#include <linux/miscdevice.h>

#include <linux/interrupt.h>

#include <linux/wait.h>

 

//1.定义个等待队列头

static wait_queue_head_t gec210_key_wq;

//3.定义一个等待队列的条件

static int gec210_key_flag = 0; //有按键按下为真(1),没有按键按下为假(0)

 

 

//定义一个buffer,存放按键的状态

static char key_buf[3] = {0,0,0}; //k2 K3 K4 没有按下

 

struct irq_info

{

char irq_name[10];

int irq_num;

};

 

struct irq_info key_irq_info[3] = {

{

.irq_name = "key2_irq",

.irq_num = IRQ_EINT(16),

},

{

.irq_name = "key3_irq",

.irq_num = IRQ_EINT(17),

},

{

.irq_name = "key4_irq",

.irq_num = IRQ_EINT(18),

},

};

 

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

{

int ret,i;

//4.判断等待条件,有按键按下,就继续读;没有按键按下,就阻塞,进程进入等待队列睡眠。

wait_event_interruptible(gec210_key_wq, gec210_key_flag);

if(len != 3)

return -EINVAL;

ret = copy_to_user(buf,key_buf,len);

if(ret < 0)

return -EFAULT;

gec210_key_flag = 0; //重置等待条件

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

key_buf[i] = 0;

return len;

}

 

 

static struct file_operations gec210_key_fops = {

.owner = THIS_MODULE,

.read  = gec210_key_read,

};

 

 

 

static struct miscdevice gec210_key_miscdev = {

.minor = MISC_DYNAMIC_MINOR, //混杂设备的次设备号,系统自动分配:MISC_DYNAMIC_MINOR

.name = "key_drv",  //misc device的名字,也是设备文件的名字

.fops = &gec210_key_fops,  //文件操作集

};

 

 

static irqreturn_t gec210_key_isr(int irq, void *dev)

{

printk("<0>""---------irq: %d isr -------- \n", irq);

if(irq == key_irq_info[0].irq_num)

{

key_buf[0] = 1;//KEY2按下

}

else if(irq == key_irq_info[1].irq_num)

{

key_buf[1] = 1;//KEY3按下

}

else if(irq == key_irq_info[2].irq_num)

{

key_buf[2] = 1;//KEY4按下

}

//5.当条件满足后,唤醒等待队列中的进程。

gec210_key_flag = 1; //当有按键按下的时候,条件设置成真

wake_up_interruptible(&gec210_key_wq);

return IRQ_HANDLED; // 表示isr已经正常执行了

}

 

 

static int __init gec210_key_irq_init(void)

{

int ret, i;

//2.初始化等待队列头

init_waitqueue_head(&gec210_key_wq);

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

{

ret = request_irq(key_irq_info[i].irq_num, gec210_key_isr,

IRQF_TRIGGER_RISING, key_irq_info[i].irq_name, NULL);

if(ret)

{

printk("request irq error \n");

goto failed_request_irq;

}

}

ret = misc_register(&gec210_key_miscdev);

if(ret < 0)

{

printk("misc register error\n");

goto failed_misc_register;

}

printk("gec210 key irq init success ! \n");

 

return 0;

failed_misc_register:

failed_request_irq:

while(i--)

free_irq(key_irq_info[i].irq_num, NULL);

return ret;

}

 

 

static void __exit gec210_key_irq_exit(void)

{

int i;

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

{

free_irq(key_irq_info[i].irq_num, NULL);

}

misc_deregister(&gec210_key_miscdev);

printk("gec210 key irq exit success ! \n");

}

 

 

module_init(gec210_key_irq_init);

module_exit(gec210_key_irq_exit);

 

 

MODULE_AUTHOR("lllllssssssssjjjjjjjjjjjjjjj");

MODULE_DESCRIPTION("the first demo of irq device");

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

MODULE_VERSION("V1.0");

 

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

2. Filename: test.c

#include <stdio.h>

#include <fcntl.h>

 

char key_status[3] = {0,0,0};

 

 

int main(void)

{

int fd,i,ret;

fd = open("/dev/key_drv", O_RDONLY);

if(fd < 0)

{

perror("open /dev/key_drv");

return -1;

}

while(1)

{

ret = read(fd, key_status, 3);

if(ret != 3)

{

perror("read");

return -1;

}

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

{

printf("key_status[%d] = %d\n",i,key_status[i]);

}

}

 

close(fd);

return 0;

}

 

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

 

3. Filename: Makefile

obj-m += key_drv.o

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
原创粉丝点击