概述
第十六章 输入子系统模型
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 specifiedn");
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 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,
};
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 watchdogn");
s3c2410wdt_keepalive(); //最后一次喂狗
}
expect_close = 0;
clear_bit(0, &open_lock);
return 0;
最后
以上就是顺利煎蛋为你收集整理的linux 驱动笔记(七)的全部内容,希望文章能够帮你解决linux 驱动笔记(七)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复