• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved.
4  */
5 
6 #define pr_fmt(fmt) "zrhung " fmt
7 
8 #include <linux/sched/clock.h>
9 #include <linux/sched/debug.h>
10 #include <linux/kernel.h>
11 #include <linux/fb.h>
12 #include <linux/notifier.h>
13 #include <linux/module.h>
14 #include <linux/string.h>
15 #include <linux/spinlock.h>
16 #include <linux/workqueue.h>
17 #include <linux/time.h>
18 #include <linux/input.h>
19 #include <linux/jiffies.h>
20 #include <linux/sched/debug.h>
21 #include <dfx/zrhung.h>
22 #include <dfx/hung_wp_screen.h>
23 
24 #define TIME_CONVERT_UNIT 1000
25 #define DEFAULT_TIMEOUT 10
26 
27 #define LPRESSEVENT_TIME 5
28 #define POWERKEYEVENT_MAX_COUNT 10
29 #define POWERKEYEVENT_DEFAULT_COUNT 3
30 #define POWERKEYEVENT_DEFAULT_TIMEWINDOW 5
31 #define POWERKEYEVENT_DEFAULT_LIMIT_MS 300
32 #define POWERKEYEVENT_DEFAULT_REPORT_MIN 2
33 #define POWERKEYEVENT_TIME_LEN (POWERKEYEVENT_MAX_COUNT + 2)
34 
35 struct hung_wp_screen_data {
36 	struct timer_list timer;
37 	struct timer_list long_press_timer;
38 	struct workqueue_struct *workq;
39 	struct work_struct send_work;
40 	spinlock_t lock;
41 	int fb_blank;
42 	int check_id;
43 	int tag_id;
44 };
45 
46 static bool init_done;
47 static struct hung_wp_screen_data g_hung_data;
48 static unsigned int lastreport_time;
49 static unsigned int lastprkyevt_time;
50 static unsigned int powerkeyevent_time[POWERKEYEVENT_TIME_LEN] = {0};
51 static unsigned int newevt;
52 static unsigned int headevt;
53 static int *check_off_point;
54 struct work_struct powerkeyevent_sendwork;
55 struct work_struct lpressevent_sendwork;
56 static struct notifier_block hung_wp_screen_setblank_ncb;
57 
zrhung_lpressevent_send_work(struct work_struct * work)58 static void zrhung_lpressevent_send_work(struct work_struct *work)
59 {
60 	pr_info("LONGPRESS_EVENT send to zerohung\n");
61 	zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_LPRESS_NAME, "none");
62 }
63 
zrhung_wp_lpress_send(struct timer_list * t)64 static void zrhung_wp_lpress_send(struct timer_list *t)
65 {
66 	int *check_off = check_off_point;
67 
68 	del_timer(&g_hung_data.long_press_timer);
69 	*check_off = 0;
70 	queue_work(g_hung_data.workq, &lpressevent_sendwork);
71 }
72 
zrhung_powerkeyevent_send_work(struct work_struct * work)73 static void zrhung_powerkeyevent_send_work(struct work_struct *work)
74 {
75 	pr_info("POWERKEY_EVENT send to zerohung\n");
76 	zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_PWK_NAME, "none");
77 }
78 
zrhung_powerkeyevent_report(unsigned int dur,unsigned int end)79 static void zrhung_powerkeyevent_report(unsigned int dur, unsigned int end)
80 {
81 	unsigned int send_interval;
82 
83 	send_interval = end > lastreport_time ?
84 	    ((end - lastreport_time) / TIME_CONVERT_UNIT) : POWERKEYEVENT_DEFAULT_REPORT_MIN;
85 	if (unlikely(lastreport_time == 0)) {
86 		lastreport_time = end;
87 	} else if (send_interval < POWERKEYEVENT_DEFAULT_REPORT_MIN) {
88 		pr_info("powerkeyevent too fast to report: %d\n", end);
89 		return;
90 	}
91 	lastreport_time = end;
92 	queue_work(g_hung_data.workq, &powerkeyevent_sendwork);
93 }
94 
refresh_prkyevt_index(unsigned int event)95 static unsigned int refresh_prkyevt_index(unsigned int event)
96 {
97 	unsigned int evt = event;
98 
99 	if (evt < POWERKEYEVENT_MAX_COUNT)
100 		evt++;
101 	else
102 		evt = 0;
103 	return evt;
104 }
105 
zrhung_new_powerkeyevent(unsigned int tmescs)106 static void zrhung_new_powerkeyevent(unsigned int tmescs)
107 {
108 	unsigned int prkyevt_interval;
109 	unsigned int evt_index;
110 	int diff;
111 
112 	powerkeyevent_time[newevt] = tmescs;
113 	evt_index = (newevt >= headevt) ?
114 	    (newevt - headevt) : (newevt + POWERKEYEVENT_MAX_COUNT + 1 - headevt);
115 	if (evt_index < (POWERKEYEVENT_DEFAULT_COUNT - 1)) {
116 		pr_info("powerkeyevent not enough-%d\n", POWERKEYEVENT_DEFAULT_COUNT);
117 	} else {
118 		diff = powerkeyevent_time[newevt] - powerkeyevent_time[headevt];
119 		if (diff < 0) {
120 			pr_info("powerkeyevent sth wrong in record time\n");
121 			return;
122 		}
123 
124 		prkyevt_interval = (unsigned int)(diff / TIME_CONVERT_UNIT);
125 		if (prkyevt_interval <= POWERKEYEVENT_DEFAULT_TIMEWINDOW)
126 			zrhung_powerkeyevent_report(prkyevt_interval, tmescs);
127 		headevt = refresh_prkyevt_index(headevt);
128 	}
129 	newevt = refresh_prkyevt_index(newevt);
130 }
131 
zrhung_powerkeyevent_handler(void)132 static void zrhung_powerkeyevent_handler(void)
133 {
134 	unsigned int curtime;
135 	unsigned long curjiff;
136 
137 	pr_info("powerkeyevent check start");
138 	curjiff = jiffies;
139 	curtime = jiffies_to_msecs(curjiff);
140 	if (unlikely(lastprkyevt_time > curtime)) {
141 		pr_info("powerkeyevent check but time overflow");
142 		lastprkyevt_time = curtime;
143 		return;
144 	} else if ((curtime - lastprkyevt_time) < POWERKEYEVENT_DEFAULT_LIMIT_MS) {
145 		pr_info("powerkeyevent user press powerkey too fast-time:%d", curtime);
146 		return;
147 	}
148 	lastprkyevt_time = curtime;
149 	zrhung_new_powerkeyevent(curtime);
150 }
151 
hung_wp_screen_setblank(struct notifier_block * self,unsigned long event,void * data)152 static int hung_wp_screen_setblank(struct notifier_block *self, unsigned long event, void *data)
153 {
154 	unsigned long flags;
155 	struct fb_event *evdata = data;
156 	int blank;
157 
158 	if (!init_done)
159 		return 0;
160 
161 	if (event != FB_EVENT_BLANK)
162 		return 0;
163 
164 	blank = *(int *)evdata->data;
165 	spin_lock_irqsave(&(g_hung_data.lock), flags);
166 	g_hung_data.fb_blank = blank;
167 	if (((g_hung_data.check_id == ZRHUNG_WP_SCREENON) && (blank == 0)) ||
168 	    ((g_hung_data.check_id == ZRHUNG_WP_SCREENOFF) && (blank != 0))) {
169 		pr_info("check_id=%d, blank=%d", g_hung_data.check_id, g_hung_data.fb_blank);
170 		del_timer(&g_hung_data.timer);
171 		g_hung_data.check_id = ZRHUNG_WP_NONE;
172 	}
173 	spin_unlock_irqrestore(&(g_hung_data.lock), flags);
174 
175 	return 0;
176 }
177 
hung_wp_screen_send_work(struct work_struct * work)178 static void hung_wp_screen_send_work(struct work_struct *work)
179 {
180 	unsigned long flags = 0;
181 
182 	show_state_filter(TASK_UNINTERRUPTIBLE);
183 
184 	if (g_hung_data.check_id == 1)
185 		zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_ON_NAME, "none");
186 	else
187 		zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_OFF_NAME, "none");
188 	pr_info("send event: %d\n", g_hung_data.check_id);
189 	spin_lock_irqsave(&(g_hung_data.lock), flags);
190 	g_hung_data.check_id = ZRHUNG_WP_NONE;
191 	spin_unlock_irqrestore(&(g_hung_data.lock), flags);
192 }
193 
hung_wp_screen_send(struct timer_list * t)194 static void hung_wp_screen_send(struct timer_list *t)
195 {
196 	del_timer(&g_hung_data.timer);
197 	pr_info("hung_wp_screen_%d end\n", g_hung_data.tag_id);
198 	queue_work(g_hung_data.workq, &g_hung_data.send_work);
199 }
200 
hung_wp_screen_start(int check_id)201 static void hung_wp_screen_start(int check_id)
202 {
203 	if (g_hung_data.check_id != ZRHUNG_WP_NONE) {
204 		pr_info("already in check_id: %d\n", g_hung_data.check_id);
205 		return;
206 	}
207 
208 	g_hung_data.check_id = check_id;
209 	if (timer_pending(&g_hung_data.timer))
210 		del_timer(&g_hung_data.timer);
211 
212 	g_hung_data.timer.expires = jiffies + msecs_to_jiffies(DEFAULT_TIMEOUT * TIME_CONVERT_UNIT);
213 	add_timer(&g_hung_data.timer);
214 	pr_info("going to check ID=%d timeout=%d\n", check_id, DEFAULT_TIMEOUT);
215 }
216 
hung_wp_screen_powerkey_ncb(int event)217 void hung_wp_screen_powerkey_ncb(int event)
218 {
219 	static int check_off;
220 	unsigned long flags = 0;
221 
222 	if (!init_done)
223 		return;
224 
225 	spin_lock_irqsave(&(g_hung_data.lock), flags);
226 	if (event == WP_SCREEN_PWK_PRESS) {
227 		pr_info("hung_wp_screen_%d start! fb_blank=%d",
228 			++g_hung_data.tag_id, g_hung_data.fb_blank);
229 		check_off = 0;
230 		if (g_hung_data.fb_blank != 0) {
231 			hung_wp_screen_start(ZRHUNG_WP_SCREENON);
232 		} else {
233 			check_off = 1;
234 			pr_info("start longpress test timer\n");
235 			check_off_point = &check_off;
236 			g_hung_data.long_press_timer.expires = jiffies +
237 				msecs_to_jiffies(LPRESSEVENT_TIME * TIME_CONVERT_UNIT);
238 			if (!timer_pending(&g_hung_data.long_press_timer))
239 				add_timer(&g_hung_data.long_press_timer);
240 		}
241 		zrhung_powerkeyevent_handler();
242 	} else if (check_off) {
243 		check_off = 0;
244 		del_timer(&g_hung_data.long_press_timer);
245 		if (event == WP_SCREEN_PWK_RELEASE && g_hung_data.fb_blank == 0)
246 			hung_wp_screen_start(ZRHUNG_WP_SCREENOFF);
247 	}
248 	spin_unlock_irqrestore(&(g_hung_data.lock), flags);
249 }
250 
hung_wp_screen_init(void)251 static int __init hung_wp_screen_init(void)
252 {
253 	init_done = false;
254 	pr_info("%s start\n", __func__);
255 	g_hung_data.fb_blank = 0;
256 	g_hung_data.tag_id = 0;
257 	g_hung_data.check_id = ZRHUNG_WP_NONE;
258 	spin_lock_init(&(g_hung_data.lock));
259 
260 	timer_setup(&g_hung_data.timer, hung_wp_screen_send, 0);
261 	timer_setup(&g_hung_data.long_press_timer, zrhung_wp_lpress_send, 0);
262 
263 	g_hung_data.workq = create_workqueue("hung_wp_screen_workq");
264 	if (g_hung_data.workq == NULL) {
265 		pr_err("create workq failed\n");
266 		return -EFAULT;
267 	}
268 	INIT_WORK(&g_hung_data.send_work, hung_wp_screen_send_work);
269 	INIT_WORK(&powerkeyevent_sendwork, zrhung_powerkeyevent_send_work);
270 	INIT_WORK(&lpressevent_sendwork, zrhung_lpressevent_send_work);
271 
272 	hung_wp_screen_setblank_ncb.notifier_call = hung_wp_screen_setblank;
273 	fb_register_client(&hung_wp_screen_setblank_ncb);
274 
275 	init_done = true;
276 	pr_info("%s done\n", __func__);
277 	return 0;
278 }
279 
hung_wp_screen_exit(void)280 static void __exit hung_wp_screen_exit(void)
281 {
282 	fb_unregister_client(&hung_wp_screen_setblank_ncb);
283 
284 	cancel_work_sync(&lpressevent_sendwork);
285 	cancel_work_sync(&powerkeyevent_sendwork);
286 	cancel_work_sync(&g_hung_data.send_work);
287 
288 	destroy_workqueue(g_hung_data.workq);
289 
290 	del_timer_sync(&g_hung_data.timer);
291 	del_timer_sync(&g_hung_data.long_press_timer);
292 }
293 
294 module_init(hung_wp_screen_init);
295 module_exit(hung_wp_screen_exit);
296 
297 MODULE_AUTHOR("OHOS");
298 MODULE_DESCRIPTION("Reporting the frozen screen alarm event");
299 MODULE_LICENSE("GPL");
300