第十六章 输入子系统模型

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的值与type和code是相关的。

如果input_event.type=EV_ABS,input_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个位(输入子系统定义了768个key或button),设备驱动中,有哪些按键,则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 分析driver的probe函数

driver和device匹配成功后,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 specified\n");

return -ENOENT;

}

2、申请物理内存区

size = resource_size(res);

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

3、ioremap得到虚拟地址

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 source\n");

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 timer\n");

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,

};

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

注意:

看门狗打开后,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的命令。

3)write函数

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 watchdog\n");

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

}

expect_close = 0;

clear_bit(0, &open_lock);

return 0;

linux 驱动笔记(七)相关推荐

  1. 嵌入式Linux驱动笔记--转自风筝丶

    为了阅读学习方便,将系列博客的网址进行粘贴,感谢原博客的分享. 嵌入式Linux驱动笔记(一)------第一个LED驱动程序 嵌入式Linux驱动笔记(二)------定时器 嵌入式Linux驱动笔 ...

  2. 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】

    转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...

  3. 嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)

    ###你好!这里是风筝的博客, ###欢迎和我一起交流. 前几天去面试,被问到Linux设备驱动模型这个问题,没答好,回来后恶补知识,找了些资料,希望下次能答出个满意答案. Linux早期时候,一个驱 ...

  4. 嵌入式Linux驱动笔记(五)------学习platform设备驱动

    你好!这里是风筝的博客, 欢迎和我一起交流. 设备是设备,驱动是驱动. 如果把两个糅合写一起,当设备发生变化时,势必要改写整个文件,这是非常愚蠢的做法.如果把他们分开来,当设备发生变化时,只要改写设备 ...

  5. 嵌入式Linux驱动笔记(二十四)------framebuffer之使用spi-tft屏幕(上)

    你好!这里是风筝的博客, 欢迎和我一起交流. 最近入手了一块spi接口的tft彩屏,想着在我的h3板子上使用framebuffer驱动起来. 我们知道: Linux抽象出FrameBuffer这个设备 ...

  6. 嵌入式Linux驱动笔记(十一)------i2c设备之mpu6050驱动

    ###你好!这里是风筝的博客, ###欢迎和我一起交流. 上一节讲了i2c框架: 嵌入式Linux驱动笔记(十)------通俗易懂式了解i2c框架 这次就来写一写真正的i2c设备驱动: mpu605 ...

  7. linux 驱动笔记(一)

    第一章 驱动概述 1 为什么要学linux驱动? linux分成内核空间和用户空间,这样对linux内核是一个保护,应用程序不能随便的访问内核,进而访问硬件. 应用程序(linuxIO编程 多进程 多 ...

  8. linux 驱动笔记(四)

    第六章 GPIO的标准接口函数 1 什么是GPIO的标准接口函数 思考: 1.1设计GPIO驱动的方法??? 1.1.1 找到配置/控制GPIO的寄存器,得到了访问该寄存器的物理地址 1.1.2 申请 ...

  9. Linux驱动笔记-TNYCL

    0.小计 IRQ:中断 RST:存储 FP:寄存器 backlight:linux背光子系统: SOC:系统及芯片,即片上系统: UART:通用异步收发传输号,串行异步收发协议,二进制按位为单位传输: ...

  10. 嵌入式Linux驱动笔记(二十七)------中断子系统框架分析

    你好!这里是风筝的博客, 欢迎和我一起交流. 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行. 从硬 ...

最新文章

  1. R语言构建xgboost模型:模型的特性重要度计算及可视化、模型对应的结构树(文本文件)
  2. 解决403跨域问题之——————JSONP
  3. linux grep命令详解
  4. if xxx.strip()函数的使用
  5. 字符串排序 java_java字符串排序
  6. LVS的工作原理和相关算法
  7. centos6.5 yum安装mysql_CentOS 6.5使用yum安装MySQL快速上手必备
  8. 代码阅读 | torch.sort函数
  9. oracle4.0,OracleTM Application Server 4.0简 介
  10. Maven插件:versions-maven-plugin
  11. git 使用http方式的一个小问题
  12. Python黑帽编程 3.4 跨越VLAN
  13. Xiaojie雷达之路---脉冲压缩
  14. IC 卡、M1 卡、CPU 卡、SAM 卡、PSAM 卡的联系与区别
  15. 提高生活、学习、工作效率的方法——时间管理Vs个人管理
  16. 威斯康星麦迪逊计算机专业排名,威斯康星大学麦迪逊分校计算机工程排名
  17. HHDBCS的快捷命令使用
  18. linux下的iic驱动程序,实战经验吐血推荐:怎样在Linux环境下轻松实现基于I2C总线的EEPROM驱动程序...
  19. java 中结束程序方法
  20. 利用python爬取股票实时信息

热门文章

  1. 紫光扫描仪ocr_清华紫光扫描仪的安装教程及使用方法
  2. esp32 支持 sd卡 micropython 文件系统_ESP32/ESP8266 MicroPython教程:将文件上传到文件系统...
  3. python 什么是原类_python中什么是类
  4. SAP license key developer access key
  5. The summary of Java
  6. Navicat设置mysql时间字段自动获取当前时间
  7. 如何快速获取设备ip地址
  8. codecademy SQL 编程系列一Introduction
  9. reflections歌词翻译_Reflections 歌词
  10. 区块链交易验证和支付验证