我是靠谱客的博主 顺利煎蛋,最近开发中收集的这篇文章主要介绍linux 驱动笔记(七),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

第十六章 输入子系统模型

 

1  什么是输入子系统模型

1.1 什么是输入子系统

学过的模型: 普通的字符设备模型cdev  混杂设备模型miscdevice  平台模型platform-----------

没有学的模型: RTC子系统模型  I2C子系统  framebuffer子系统  OSS/ALSA子系统  MTD子系统  USB子系统        

 

输入子系统:输入类型的设备采用输入系统模型来设计,可以降低驱动的设计难度,还可以给应用程序一个标准的接口。

输入子系统也是普通字符设备的一个封装,如miscdevice,但是输入子系统主要是应用于输入类型设备的驱动开发。

 

1.2 什么硬件设备可以使用输入子系统模型来设计

1.2.1按键

1.2.2鼠标

1.2.3键盘

1.2.4触摸屏

1.2.5手写板

1.2.6游戏手柄

 

1.3 GEC210平台上的输入设备

1.3.1查看设备文件

# ls /dev/* -l

crw-rw----    1 root     root       13,  64 Jan  1 12:01 /dev/event0  //触摸屏

crw-rw----    1 root     root       13,  65 Jan  1 12:04 /dev/event1  //鼠标

crw-rw----    1 root     root       13,  63 Jan  1 12:01 /dev/mice

crw-rw----    1 root     root       13,  32 Jan  1 12:01 /dev/mouse0

crw-rw----    1 root     root       13,  33 Jan  1 12:04 /dev/mouse1

 

1.3.2查看设备的信息

[root@GEC210 /]# cat /proc/bus/input/devices

I: Bus=0000 Vendor=0000 Product=0000 Version=0000

N: Name="ft5x0x_ts"  //电容式触摸屏。 电阻屏--->TSC2007

P: Phys=

S: Sysfs=/devices/virtual/input/input0

U: Uniq=

H: Handlers=mouse0 event0

B: EV=b

B: KEY=400 0 0 0 0 0 0 0 0 0 0

B: ABS=1000003

 

I: Bus=0003 Vendor=093a Product=2510 Version=0111

N: Name="PixArt USB Optical Mouse"

P: Phys=usb-s5pv210-1.3/input0

S: Sysfs=/devices/platform/s5p-ehci/usb1/1-1/1-1.3/1-1.3:1.0/input/input4

U: Uniq=

H: Handlers=mouse1 event1

B: EV=17

B: KEY=ff0000 0 0 0 0 0 0 0 0

B: REL=103

B: MSC=10

 

1.4 查看ubuntu系统上的输入子系统信息

1.4.1查看设备文件

gec@ubuntu:/dev/input$ ls -l

total 0

crw-r----- 1 root root 13, 64 Sep 28 20:00 event0

crw-r----- 1 root root 13, 65 Sep 28 20:00 event1

crw-r----- 1 root root 13, 66 Sep 28 20:00 event2

crw-r----- 1 root root 13, 67 Sep 28 20:00 event3

crw-r----- 1 root root 13, 68 Sep 28 20:00 event4

crw-r--r-- 1 root root 13,  0 Sep 28 20:00 js0

crw-r----- 1 root root 13, 63 Sep 28 20:00 mice

crw-r----- 1 root root 13, 32 Sep 28 20:00 mouse0

crw-r----- 1 root root 13, 33 Sep 28 20:00 mouse1

crw-r----- 1 root root 13, 34 Sep 28 20:00 mouse2

 

1.4.2查看输入设备

$ cat /proc/bus/input/devices

I: Bus=0019 Vendor=0000 Product=0001 Version=0000

N: Name="Power Button"  //电源开关按钮

P: Phys=LNXPWRBN/button/input0

S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0

U: Uniq=

H: Handlers=kbd event0

B: PROP=0

B: EV=3

B: KEY=100000 0 0 0

 

I: Bus=0011 Vendor=0001 Product=0001 Version=ab41

N: Name="AT Translated Set 2 keyboard"  //键盘

P: Phys=isa0060/serio0/input0

S: Sysfs=/devices/platform/i8042/serio0/input/input1

U: Uniq=

H: Handlers=sysrq kbd event1

B: PROP=0

B: EV=120013

B: KEY=4 2000000 3803078 f800d001 feffffdf ffefffff ffffffff fffffffe

B: MSC=10

B: LED=7

 

I: Bus=0003 Vendor=0e0f Product=0003 Version=0110

N: Name="VMware VMware Virtual USB Mouse"  //鼠标

P: Phys=usb-0000:02:00.0-1/input0

S: Sysfs=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0/input/input2

U: Uniq=

H: Handlers=mouse0 event2 js0

B: PROP=0

B: EV=1f

B: KEY=ffff0000 0 0 0 0 0 0 0 0

B: REL=140

B: ABS=3

B: MSC=10

 

I: Bus=0003 Vendor=0e0f Product=0003 Version=0110

N: Name="VMware VMware Virtual USB Mouse"

P: Phys=usb-0000:02:00.0-1/input1

S: Sysfs=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.1/input/input3

U: Uniq=

H: Handlers=mouse1 event3

B: PROP=0

B: EV=17

B: KEY=ffff0000 0 0 0 0 0 0 0 0

B: REL=143

B: MSC=10

 

I: Bus=0011 Vendor=0002 Product=0005 Version=0000

N: Name="ImPS/2 Generic Wheel Mouse"

P: Phys=isa0060/serio1/input0

S: Sysfs=/devices/platform/i8042/serio1/input/input4

U: Uniq=

H: Handlers=mouse2 event4

B: PROP=0

B: EV=7

B: KEY=70000 0 0 0 0 0 0 0 0

B: REL=103

 

注意:

一般输入设备的设备文件/dev/event0/dev/intput/event0,与根文件系统的设计有关系。

 

1.5 读取输入设备的数据

#cat /dev/event1

 

2  输入子系统应用程序从驱动程序读出的数据----struct input_event

2.1  struct input_event

前面提到:采用输入子系统模型设备的驱动程序,给应用程序传递的数据是一个标准的接口数据。

 

是一个结构体struct input_event

struct input_event {

struct timeval time;

__u16 type;

__u16 code;

__s32 value;

};

 

成员说明:

struct timeval time ---> 输入事件发生的时间

__u16 type ---> 输入事件的类型

__u16 code ---> 在输入事件类型下的编码

__s32 value ---> code的值

2.1.1输入事件的类型--input_event.type

输入事件的类型是与设备设备有关系的,常见的输入事件类型:

#define EV_SYN 0x00  //--->同步事件(每次读取驱动的数据,都会有一个同步)

#define EV_KEY 0x01  //--->键盘  键盘

#define EV_REL 0x02  //--->相对坐标,鼠标

#define EV_ABS 0x03  //--->绝对坐标,触摸屏

#define EV_MSC 0x04

#define EV_SW 0x05

#define EV_LED 0x11

#define EV_SND 0x12

#define EV_REP 0x14

#define EV_FF 0x15

#define EV_PWR 0x16

#define EV_FF_STATUS 0x17

#define EV_MAX 0x1f

#define EV_CNT (EV_MAX+1)

 

2.1.2输入事件的编码---input_event.code

code是和type是相关的:

 

如果input_event.type=EV_ABS,则code是触摸屏的轴

code的值:

#define ABS_X 0x00  //X

#define ABS_Y 0x01  //Y

#define ABS_Z 0x02  //Z/压力

 

如果input_event.type=EV_KEY,则code是键盘的某个按键(注意:每个按键有自己的宏定义的值)

#define KEY_1 2

#define KEY_2 3

#define KEY_3 4

 

#define KEY_Q 16

#define KEY_W 17

#define KEY_E 18

#define KEY_R 19

#define KEY_T 20

#define KEY_F1 59

#define KEY_F2 60

#define KEY_F3 61

 

2.2.3输入事件的值--input_event.value

value的值与typecode是相关的。

如果input_event.type=EV_ABSinput_event.code=ABS_X,

value是触摸屏X轴的坐标值。

 

如果input_event.type=EV_KEY,input_event.code=0x20,

则代表KEY_D按键有输入,这个时候value代表的是按键的状态:

Value = 1  按键按下

        0  按键松开

2  按键长按

 

2.2  如何读取触摸屏的坐标

应用程序:

直接读取触摸屏坐标的步骤:

#include <linux/input.h> //使用输入子系统的时候,包含的一个头文件

struct input_event gec210_ts_event;

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

read(fd, &gec210_ts_event, sizeof(struct input_event));

分析gec210_ts_event,得到触摸屏的坐标

gec210_ts_event.type --->是不是触摸屏

gec210_ts_event.code --->触摸屏的轴

gec210_ts_event.value --->触摸屏的坐标值

 

close(fd);

 

中午作业:

编写一个应用程序,读取触摸屏的坐标。(不适用tslib的库)

 

注意1

    在输入子系统的模型中,是有一个等待队列,如果输入事件没有发生(按键没有按下  触摸屏没有触摸  鼠标没有移动),应用程序就会产生阻塞,进入睡眠状态。当输入事件发生的时候,应用程序被唤醒。

我们设计驱动的时候,不需要加等待队列,该队列在输入子系统中。

 

注意2

    得到一个触摸屏坐标的时候,应该读两次触摸屏的驱动,一次是X轴,另一次是Y轴。

 

注意3

    采用输入子系统模型设计驱动程序,驱动程序和应用程序之间的数据是统一的数据格式---struct input_event

 

3  输入子系统驱动程序的框架

 

流程:(以按键为例)

 

3.1 有中断的情况下:

按下按键--->触发外部中断(双边沿触发)-->响应中断服务程序--->ISR中得到按键的状态(1-按下  0-松开)--->将输入事件(type  code  value)报告给输入子系统的核心层--->事件再传到输入子系统的事件处理层--->设备文件 ----> 应用程序读取设备文件中的输入事件,得到数据-----struct input_event

 

3.2 没有中断的情况下:

内核动态定时器--->超时时间和超时处理函数--->在超时处理函数中,扫描按键的状态--->按键的状态有变化,确认按键是按下还是松开--->将输入事件(type  code  value)报告给输入子系统的核心层--->事件再传到输入子系统的事件处理层--->设备文件 ----> 应用程序读取设备文件中的输入事件,得到数据-----struct input_event

 

4  输入子系统设备驱动的设计流程

 

4.1 定义一个输入子系统设备

struct input_dev {

const char *name; //输入设备的名字 #cat /proc/bus/input/devices,不是设备文件名

const char *phys;

const char *uniq;

struct input_id id;   //厂商代码  产品版本等

 

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];     //type:设置输入事件的类型:键盘  按键  鼠标  触摸屏

unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];   //code:如果类型是EV_KEY,keybit设置有哪些按键

unsigned long relbit[BITS_TO_LONGS(REL_CNT)];   //code:如果类型是EV_REL,relbit设置有哪些相对坐标

unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];

unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];

unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];

unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];

unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];

unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

 

.............

 

struct list_head node;

};

 

例:

 

static struct input_dev *gec210_key;

 

4.2 给输入设备分配空间,并做基本的初始化

/**

 * input_allocate_device - allocate memory for new input device

 *

 * Returns prepared struct input_dev or NULL.

 *

 * NOTE: Use input_free_device() to free devices that have not been

 * registered; input_unregister_device() should be used for already

 * registered devices.

 */

struct input_dev *input_allocate_device(void);

注意:

当调用input_allocate_device()分配空间后,输入设备没有注册成功,则使用input_free_device()释放已经分配好的空间。如果输入设备注册成功了,在卸载驱动的时候,则input_unregister_device()会自动的释放input_dev的内存空间,就不需要使用input_free_device() 这个函数。

 

void input_free_device(struct input_dev *dev);

gec210_key = input_allocate_device();

 

4.3 输入设备详细初始化(有难度)

 

1)设置输入设备的类型-->evbit[]

set_bit(EV_KEY,gec210_key->evbit)

 

相当于:

gec210_key->evbit[BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY);

 

 

分析:

#define BITS_PER_LONG 32

#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)

则:              

BIT_WORD(EV_KEY) --> (1)/32 = 0

 

#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))

则:

BIT_MASK(EV_KEY) --> 1<<(1%32)  = 1<<1

 

结论:

gec210_key->evbit[0] = (1<<1);

 

作用:

evbit[]用来描述输入子系统设备驱动程序的类型,其中evbit[]每个位代表一个类型,0-->EV_SYN,1--EV_KEY.

 

2)设置该类型下的code

如果我们设计的是按键/键盘的驱动,则gec210_key->evbit[BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY);

,在这种情况下:

unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];设置的是code,即该驱动中有哪些按键。keybit[]768个位(输入子系统定义了768keybutton),设备驱动中,有哪些按键,则keybit[]的对应为就初始化成1.

 

例:GEC210平台: K2---GPH2_0----EINT16 --->KEY_A

       K3---GPH2_1----EINT17 --->KEY_B

                K4---GPH2_2----EINT18 --->KEY_C

        K5---GPH2_3----EINT19 --->KEY_D

使用四个不同的按键值(KEY_A~KEY_D)来代表四个不同按键(K2~K5),如何初始化keybit[]???

如下:

gec210_key->keybit[BIT_WORD(KEY_A)] = BIT_MASK(KEY_A); //keybit[0]=(1<<30)

gec210_key->keybit[BIT_WORD(KEY_B)] |= BIT_MASK(KEY_B); //keybit[1]|=(1<<16)

gec210_key->keybit[BIT_WORD(KEY_C)] |= BIT_MASK(KEY_C); //keybit[1]|=(1<<14)

gec210_key->keybit[BIT_WORD(KEY_D)] |= BIT_MASK(KEY_D); //keybit[1]=(1<<0)

等价于:

set_bit(KEY_A,gec210_key->keybit)

set_bit(KEY_B,gec210_key->keybit)

set_bit(KEY_C,gec210_key->keybit)

set_bit(KEY_D,gec210_key->keybit)

 

3)初始化一些驱动的厂商信息(非必要)

即:#cat /proc/bus/input/devices 看到的信息

gec210_key->name = "gec210_keys";

gec210_key->id.bustype = 0x01;

gec210_key->id.vendor = 0x02;

gec210_key->id.product = 0x03;

gec210_key->id.version = 0x04;

 

4.4 将输入设备注册到内核

int input_register_device(struct input_dev *dev)

 

反函数:

void input_unregister_device(struct input_dev *dev)

 

4.5 注册中断

static inline int __must_check

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

    const char *name, void *dev)

注意:

    unsigned long flags  ---->设置双边沿触发

 

4.6 中断的中断服务程序

static irqreturn_t gec210_key_isr(int irq, void *dummy)

 

{

if(irq == EINT16) //K2

{

//使用request_irq()后,可以直接使用gpio_get_value()得到GPIO输入值

key_value = gpio_get_value(S5PV210_GPH2(0)); //0-->按下。1-->松开

input_report_key(gec210_key, KEY_A, !key_value); //!--??????

}

input_sync(gec210_key);

 //每次report之后,都需要一次同步。

return IRQ_HANDLED;

}

 

void input_report_key(struct input_dev *dev, unsigned int code, int value)

参数说明:

struct input_dev *dev ---> 输入设备

unsigned int code ---> 哪一个按键

int value ---> 按键的状态0  1  2

 

注意:

不用设计文件操作集,系统做好了

自动生成设备文件

 

作业:

 

例:GEC210平台:

K2 --- GPH2_0 ---- EINT16 ---> KEY_A

    K3 --- GPH2_1 ---- EINT17 ---> KEY_B

        K4 --- GPH2_2 ---- EINT18 ---> KEY_C

    K5 --- GPH2_3 ---- EINT19 ---> KEY_D

 

第十七章 看门狗

 

1针对硬件平台的主初始化源文件

linux/arch/arm/mach-s5pv210/mach-smdkc110.c

linux/arch/arm/mach-s5pv210/mach-gec210.c

 

2主初始化源文件中的机器宏

 

MACHINE_START(GEC210, "GEC210")

/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */

.phys_io = S3C_PA_UART & 0xfff00000,

.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,

.boot_params = S5P_PA_SDRAM + 0x100,

.init_irq = s5pv210_init_irq,

.map_io = smdkc110_map_io,

.init_machine = smdkc110_machine_init,

.timer = &s5p_systimer,

MACHINE_END

 

3主初始化函数---smdkc110_machine_init()

linux内核在启动的过程中,会调用主初始化函数,利用主初始化函数来配置硬件。

 

static void __init smdkc110_machine_init(void)

{

.......................................................

platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));

......................

}

 

4 platform device的安装注册函数

int platform_add_devices(struct platform_device **devs, int num)

{

int i, ret = 0;

 

for (i = 0; i < num; i++) {

ret = platform_device_register(devs[i]);

if (ret) {

while (--i >= 0)

platform_device_unregister(devs[i]);

break;

}

}

 

return ret;

}

 

5 找到platform device的总表

 

static struct platform_device *smdkc110_devices[] __initdata = {

#ifdef CONFIG_FIQ_DEBUGGER

&s5pv210_device_fiqdbg_uart2,

#endif

#ifdef CONFIG_MTD_ONENAND

&s5pc110_device_onenand,

#endif

#ifdef CONFIG_MTD_NAND

&s3c_device_nand,

#endif

&s5p_device_rtc,

#ifdef CONFIG_SND_S3C64XX_SOC_I2S_V4

&s5pv210_device_iis0,

#endif

#ifdef CONFIG_SND_S3C_SOC_AC97

&s5pv210_device_ac97,

#endif

#ifdef CONFIG_SND_S3C_SOC_PCM

&s5pv210_device_pcm0,

#endif

#ifdef CONFIG_SND_SOC_SPDIF

&s5pv210_device_spdif,

#endif

&s3c_device_wdt,

 

#ifdef CONFIG_FB_S3C

&s3c_device_fb,

#endif

#ifdef CONFIG_DM9000

#if 0

&s5p_device_dm9000,

#else

&gec210_device_dm9000,

#endif

#endif

 

#ifdef CONFIG_VIDEO_MFC50

&s3c_device_mfc,

#endif

#ifdef CONFIG_TOUCHSCREEN_S3C

&s3c_device_ts,

#endif

&s3c_device_keypad,

#ifdef CONFIG_S5P_ADC

&s3c_device_adc,

#endif

#ifdef CONFIG_VIDEO_FIMC

&s3c_device_fimc0,

&s3c_device_fimc1,

&s3c_device_fimc2,

#endif

#ifdef CONFIG_VIDEO_FIMC_MIPI

&s3c_device_csis,

#endif

#ifdef CONFIG_VIDEO_JPEG_V2

&s3c_device_jpeg,

#endif

#ifdef CONFIG_VIDEO_G2D

&s3c_device_g2d,

#endif

#ifdef CONFIG_VIDEO_TV20

&s5p_device_tvout,

&s5p_device_cec,

&s5p_device_hpd,

#endif

 

&s3c_device_g3d,

&s3c_device_lcd,

 

&s3c_device_i2c0,

#ifdef CONFIG_S3C_DEV_I2C1

&s3c_device_i2c1,

#endif

#ifdef CONFIG_S3C_DEV_I2C2

&s3c_device_i2c2,

#endif

 

#ifdef CONFIG_USB_EHCI_HCD

&s3c_device_usb_ehci,

#endif

#ifdef CONFIG_USB_OHCI_HCD

&s3c_device_usb_ohci,

#endif

 

#ifdef CONFIG_USB_GADGET

&s3c_device_usbgadget,

#endif

#ifdef CONFIG_USB_ANDROID

&s3c_device_android_usb,

#ifdef CONFIG_USB_ANDROID_MASS_STORAGE

&s3c_device_usb_mass_storage,

#endif

#ifdef CONFIG_USB_ANDROID_RNDIS

&s3c_device_rndis,

#endif

#endif

#ifdef CONFIG_BATTERY_S3C

&sec_device_battery,

#endif

#ifdef CONFIG_S3C_DEV_HSMMC

&s3c_device_hsmmc0,

#endif

#ifdef CONFIG_S3C_DEV_HSMMC1

&s3c_device_hsmmc1,

#endif

#ifdef CONFIG_S3C_DEV_HSMMC2

&s3c_device_hsmmc2,

#endif

#ifdef CONFIG_S3C_DEV_HSMMC3

&s3c_device_hsmmc3,

#endif

 

#ifdef CONFIG_S3C64XX_DEV_SPI

&s5pv210_device_spi0,

&s5pv210_device_spi1,

#endif

#ifdef CONFIG_S5PV210_POWER_DOMAIN

&s5pv210_pd_audio,

&s5pv210_pd_cam,

&s5pv210_pd_tv,

&s5pv210_pd_lcd,

&s5pv210_pd_g3d,

&s5pv210_pd_mfc,

#endif

 

#ifdef CONFIG_HAVE_PWM

&s3c_device_timer[0],

&s3c_device_timer[1],

&s3c_device_timer[2],

&s3c_device_timer[3],

#endif

};

 

6 找到看门狗的platform device

 

&s3c_device_wdt,

 

7 找到s3c_device_wdt

linux/arch/arm/plat-samsung/dev-wdt.c

static struct resource s3c_wdt_resource[] = {

[0] = {

.start = S3C_PA_WDT,

.end = S3C_PA_WDT + SZ_1M - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_WDT,

.end = IRQ_WDT,

.flags = IORESOURCE_IRQ,

}

};

 

struct platform_device s3c_device_wdt = {

.name = "s3c2410-wdt",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_wdt_resource),

.resource = s3c_wdt_resource,

};

EXPORT_SYMBOL(s3c_device_wdt);

 

 

8 根据device找到driver(同名)

 linux/drivers/watchdog/s3c2410_wdt.c

static struct platform_driver s3c2410wdt_driver = {

.probe = s3c2410wdt_probe,

.remove = __devexit_p(s3c2410wdt_remove),

.shutdown = s3c2410wdt_shutdown,

.suspend = s3c2410wdt_suspend,

.resume = s3c2410wdt_resume,

.driver = {

.owner = THIS_MODULE,

.name = "s3c2410-wdt",

},

};

 

9 分析driverprobe函数

driverdevice匹配成功后,bus会调用driver中的probe函数,实现驱动的安装及初始化。

 

static int __devinit s3c2410wdt_probe(struct platform_device *pdev)

{

1、获取资源(IO内存)

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (res == NULL) {

dev_err(dev, "no memory resource specifiedn");

return -ENOENT;

}

 

2、申请物理内存区

size = resource_size(res);

wdt_mem = request_mem_region(res->start, size, pdev->name);

 

3ioremap得到虚拟地址

wdt_base = ioremap(res->start, size);

 

 

4、获取资源(中断)

wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

 

 

5、申请中断

ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);

 

6、获取时钟,并打开时钟

wdt_clock = clk_get(&pdev->dev, "watchdog");

if (IS_ERR(wdt_clock)) {

dev_err(dev, "failed to find watchdog clock sourcen");

ret = PTR_ERR(wdt_clock);

goto err_irq;

}

 

clk_enable(wdt_clock);

 

7、设置看门狗的复位时间----默认是15

    设置看门狗的基准频率和计数值。

if (s3c2410wdt_set_heartbeat(tmr_margin)) {

started = s3c2410wdt_set_heartbeat(

CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

 

 

8、注册混杂设备

ret = misc_register(&s3c2410wdt_miscdev);

 

 

9、关闭看门狗

if (tmr_atboot && (started == 0)) {

dev_info(dev, "starting watchdog timern");

s3c2410wdt_start();

} else if (!tmr_atboot) {

/* if we're not enabling the watchdog, then ensure it is

 * disabled if it has been left running from the bootloader

 * or other source */

 

s3c2410wdt_stop();

}

 

 

10、混杂设备

static struct miscdevice s3c2410wdt_miscdev = {

.minor = WATCHDOG_MINOR,

.name = "watchdog",

.fops = &s3c2410wdt_fops,

};

 

11、文件操作集

static const struct file_operations s3c2410wdt_fops = {

.owner = THIS_MODULE,

.llseek = no_llseek,

.write = s3c2410wdt_write,

.unlocked_ioctl = s3c2410wdt_ioctl,

.open = s3c2410wdt_open,

.release = s3c2410wdt_release,

};

 

1open函数---打开看门狗

注意:

看门狗打开后,15秒之内必须要给看门狗喂狗,否则系统会重启。

如何喂狗:向WTCNT寄存器写入计数初始值。

static int s3c2410wdt_open(struct inode *inode, struct file *file)

{

if (test_and_set_bit(0, &open_lock))

return -EBUSY;

 

if (nowayout)

__module_get(THIS_MODULE);

 

expect_close = 0;

 

/* start the timer */

s3c2410wdt_start();

return nonseekable_open(inode, file);

}

 

2)ioctl函数

static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,

unsigned long arg)

{

void __user *argp = (void __user *)arg;

int __user *p = argp;

int new_margin;

 

switch (cmd) {

case WDIOC_GETSUPPORT:

return copy_to_user(argp, &s3c2410_wdt_ident,

sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;

case WDIOC_GETSTATUS:

case WDIOC_GETBOOTSTATUS:

return put_user(0, p);

case WDIOC_KEEPALIVE:  ---->看门狗喂狗

s3c2410wdt_keepalive();

return 0;

case WDIOC_SETTIMEOUT: ---->设置看门狗的复位时间

if (get_user(new_margin, p))

return -EFAULT;

if (s3c2410wdt_set_heartbeat(new_margin))

return -EINVAL;

s3c2410wdt_keepalive();

return put_user(tmr_margin, p);

case WDIOC_GETTIMEOUT: ---->查看看门狗的复位时间

return put_user(tmr_margin, p);

default:

return -ENOTTY;

}

}

 

注意:

在编写看门狗驱动的应用程序的时候,我们在应用程序中包换头文件

#include <linux/watchdog.h>

就可以直接使用ioctl的命令。

 

 

3write函数

static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,

size_t len, loff_t *ppos)

{

/*

 * Refresh the timer.

 */

if (len) {

if (!nowayout) { //nowayout的初始值是0

size_t i;

 

/* In case it was set long ago */

expect_close = 0;

 

for (i = 0; i != len; i++) {

char c;

 

if (get_user(c, data + i)) //一个字节一个字节地接收应用程序的数据

return -EFAULT;

if (c == 'V') //如果接收到的数据中,有字符'V'

expect_close = 42; //?????????????

}

}

s3c2410wdt_keepalive(); //看门狗喂狗

}

return len;

}

 

4)release函数

static int s3c2410wdt_release(struct inode *inode, struct file *file)

{

/*

 * Shut off the timer.

 * Lock it in if it's a module and we set nowayout

 */

 

if (expect_close == 42) //应用程序向驱动程序写入的数据包含'V'

s3c2410wdt_stop();  //看门狗关闭

else {

dev_err(wdt_dev, "Unexpected close, not stopping watchdogn");

s3c2410wdt_keepalive(); //最后一次喂狗

}

expect_close = 0;

clear_bit(0, &open_lock);

return 0;

最后

以上就是顺利煎蛋为你收集整理的linux 驱动笔记(七)的全部内容,希望文章能够帮你解决linux 驱动笔记(七)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部