example-codelock.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. * Copyright (c) 2004-2005, Swedish Institute of Computer Science.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the Institute nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. *
  29. * This file is part of the protothreads library.
  30. *
  31. * Author: Adam Dunkels <adam@sics.se>
  32. *
  33. * $Id: example-codelock.c,v 1.5 2005/10/06 07:57:08 adam Exp $
  34. */
  35. /*
  36. *
  37. * This example shows how to implement a simple code lock. The code
  38. * lock waits for key presses from a numeric keyboard and if the
  39. * correct code is entered, the lock is unlocked. There is a maximum
  40. * time of one second between each key press, and after the correct
  41. * code has been entered, no more keys must be pressed for 0.5 seconds
  42. * before the lock is opened.
  43. *
  44. * This is an example that shows two things:
  45. * - how to implement a code lock key input mechanism, and
  46. * - how to implement a sequential timed routine.
  47. *
  48. * The program consists of two protothreads, one that implements the
  49. * code lock reader and one that implements simulated keyboard input.
  50. *
  51. *
  52. */
  53. #ifdef _WIN32
  54. #include <windows.h>
  55. #else
  56. #include <unistd.h>
  57. #include <sys/time.h>
  58. #endif
  59. #include <stdio.h>
  60. #include "pt.h"
  61. /*---------------------------------------------------------------------------*/
  62. /*
  63. * The following definitions are just for the simple timer library
  64. * used in this example. The actual implementation of the functions
  65. * can be found at the end of this file.
  66. */
  67. struct timer { int start, interval; };
  68. static int timer_expired(struct timer *t);
  69. static void timer_set(struct timer *t, int usecs);
  70. /*---------------------------------------------------------------------------*/
  71. /*
  72. * This example uses two timers: one for the code lock protothread and
  73. * one for the simulated key input protothread.
  74. */
  75. static struct timer codelock_timer, input_timer;
  76. /*---------------------------------------------------------------------------*/
  77. /*
  78. * This is the code that has to be entered.
  79. */
  80. static const char code[4] = {'1', '4', '2', '3'};
  81. /*---------------------------------------------------------------------------*/
  82. /*
  83. * This example has two protothread and therefor has two protothread
  84. * control structures of type struct pt. These are initialized with
  85. * PT_INIT() in the main() function below.
  86. */
  87. static struct pt codelock_pt, input_pt;
  88. /*---------------------------------------------------------------------------*/
  89. /*
  90. * The following code implements a simple key input. Input is made
  91. * with the press_key() function, and the function key_pressed()
  92. * checks if a key has been pressed. The variable "key" holds the
  93. * latest key that was pressed. The variable "key_pressed_flag" is set
  94. * when a key is pressed and cleared when a key press is checked.
  95. */
  96. static char key, key_pressed_flag;
  97. static void
  98. press_key(char k)
  99. {
  100. printf("--- Key '%c' pressed\n", k);
  101. key = k;
  102. key_pressed_flag = 1;
  103. }
  104. static int
  105. key_pressed(void)
  106. {
  107. if(key_pressed_flag != 0) {
  108. key_pressed_flag = 0;
  109. return 1;
  110. }
  111. return 0;
  112. }
  113. /*---------------------------------------------------------------------------*/
  114. /*
  115. * Declaration of the protothread function implementing the code lock
  116. * logic. The protothread function is declared using the PT_THREAD()
  117. * macro. The function is declared with the "static" keyword since it
  118. * is local to this file. The name of the function is codelock_thread
  119. * and it takes one argument, pt, of the type struct pt.
  120. *
  121. */
  122. static
  123. PT_THREAD(codelock_thread(struct pt *pt))
  124. {
  125. /* This is a local variable that holds the number of keys that have
  126. * been pressed. Note that it is declared with the "static" keyword
  127. * to make sure that the variable is *not* allocated on the stack.
  128. */
  129. static int keys;
  130. /*
  131. * Declare the beginning of the protothread.
  132. */
  133. PT_BEGIN(pt);
  134. /*
  135. * We'll let the protothread loop until the protothread is
  136. * expliticly exited with PT_EXIT().
  137. */
  138. while(1) {
  139. /*
  140. * We'll be reading key presses until we get the right amount of
  141. * correct keys.
  142. */
  143. for(keys = 0; keys < sizeof(code); ++keys) {
  144. /*
  145. * If we haven't gotten any keypresses, we'll simply wait for one.
  146. */
  147. if(keys == 0) {
  148. /*
  149. * The PT_WAIT_UNTIL() function will block until the condition
  150. * key_pressed() is true.
  151. */
  152. PT_WAIT_UNTIL(pt, key_pressed());
  153. } else {
  154. /*
  155. * If the "key" variable was larger than zero, we have already
  156. * gotten at least one correct key press. If so, we'll not
  157. * only wait for the next key, but we'll also set a timer that
  158. * expires in one second. This gives the person pressing the
  159. * keys one second to press the next key in the code.
  160. */
  161. timer_set(&codelock_timer, 1000);
  162. /*
  163. * The following statement shows how complex blocking
  164. * conditions can be easily expressed with protothreads and
  165. * the PT_WAIT_UNTIL() function.
  166. */
  167. PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer));
  168. /*
  169. * If the timer expired, we should break out of the for() loop
  170. * and start reading keys from the beginning of the while(1)
  171. * loop instead.
  172. */
  173. if(timer_expired(&codelock_timer)) {
  174. printf("Code lock timer expired.\n");
  175. /*
  176. * Break out from the for() loop and start from the
  177. * beginning of the while(1) loop.
  178. */
  179. break;
  180. }
  181. }
  182. /*
  183. * Check if the pressed key was correct.
  184. */
  185. if(key != code[keys]) {
  186. printf("Incorrect key '%c' found\n", key);
  187. /*
  188. * Break out of the for() loop since the key was incorrect.
  189. */
  190. break;
  191. } else {
  192. printf("Correct key '%c' found\n", key);
  193. }
  194. }
  195. /*
  196. * Check if we have gotten all keys.
  197. */
  198. if(keys == sizeof(code)) {
  199. printf("Correct code entered, waiting for 500 ms before unlocking.\n");
  200. /*
  201. * Ok, we got the correct code. But to make sure that the code
  202. * was not just a fluke of luck by an intruder, but the correct
  203. * code entered by a person that knows the correct code, we'll
  204. * wait for half a second before opening the lock. If another
  205. * key is pressed during this time, we'll assume that it was a
  206. * fluke of luck that the correct code was entered the first
  207. * time.
  208. */
  209. timer_set(&codelock_timer, 500);
  210. PT_WAIT_UNTIL(pt, key_pressed() || timer_expired(&codelock_timer));
  211. /*
  212. * If we continued from the PT_WAIT_UNTIL() statement without
  213. * the timer expired, we don't open the lock.
  214. */
  215. if(!timer_expired(&codelock_timer)) {
  216. printf("Key pressed during final wait, code lock locked again.\n");
  217. } else {
  218. /*
  219. * If the timer expired, we'll open the lock and exit from the
  220. * protothread.
  221. */
  222. printf("Code lock unlocked.\n");
  223. PT_EXIT(pt);
  224. }
  225. }
  226. }
  227. /*
  228. * Finally, we'll mark the end of the protothread.
  229. */
  230. PT_END(pt);
  231. }
  232. /*---------------------------------------------------------------------------*/
  233. /*
  234. * This is the second protothread in this example. It implements a
  235. * simulated user pressing the keys. This illustrates how a linear
  236. * sequence of timed instructions can be implemented with
  237. * protothreads.
  238. */
  239. static
  240. PT_THREAD(input_thread(struct pt *pt))
  241. {
  242. PT_BEGIN(pt);
  243. printf("Waiting 1 second before entering first key.\n");
  244. timer_set(&input_timer, 1000);
  245. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  246. press_key('1');
  247. timer_set(&input_timer, 100);
  248. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  249. press_key('2');
  250. timer_set(&input_timer, 100);
  251. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  252. press_key('3');
  253. timer_set(&input_timer, 2000);
  254. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  255. press_key('1');
  256. timer_set(&input_timer, 200);
  257. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  258. press_key('4');
  259. timer_set(&input_timer, 200);
  260. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  261. press_key('2');
  262. timer_set(&input_timer, 2000);
  263. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  264. press_key('3');
  265. timer_set(&input_timer, 200);
  266. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  267. press_key('1');
  268. timer_set(&input_timer, 200);
  269. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  270. press_key('4');
  271. timer_set(&input_timer, 200);
  272. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  273. press_key('2');
  274. timer_set(&input_timer, 100);
  275. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  276. press_key('3');
  277. timer_set(&input_timer, 100);
  278. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  279. press_key('4');
  280. timer_set(&input_timer, 1500);
  281. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  282. press_key('1');
  283. timer_set(&input_timer, 300);
  284. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  285. press_key('4');
  286. timer_set(&input_timer, 400);
  287. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  288. press_key('2');
  289. timer_set(&input_timer, 500);
  290. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  291. press_key('3');
  292. timer_set(&input_timer, 2000);
  293. PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
  294. PT_END(pt);
  295. }
  296. /*---------------------------------------------------------------------------*/
  297. /*
  298. * This is the main function. It initializes the two protothread
  299. * control structures and schedules the two protothreads. The main
  300. * function returns when the protothread the runs the code lock exits.
  301. */
  302. int
  303. main(void)
  304. {
  305. /*
  306. * Initialize the two protothread control structures.
  307. */
  308. PT_INIT(&input_pt);
  309. PT_INIT(&codelock_pt);
  310. /*
  311. * Schedule the two protothreads until the codelock_thread() exits.
  312. */
  313. while(PT_SCHEDULE(codelock_thread(&codelock_pt))) {
  314. PT_SCHEDULE(input_thread(&input_pt));
  315. /*
  316. * When running this example on a multitasking system, we must
  317. * give other processes a chance to run too and therefore we call
  318. * usleep() resp. Sleep() here. On a dedicated embedded system,
  319. * we usually do not need to do this.
  320. */
  321. #ifdef _WIN32
  322. Sleep(0);
  323. #else
  324. usleep(10);
  325. #endif
  326. }
  327. return 0;
  328. }
  329. /*---------------------------------------------------------------------------*/
  330. /*
  331. * Finally, the implementation of the simple timer library follows.
  332. */
  333. #ifdef _WIN32
  334. static int clock_time(void)
  335. { return (int)GetTickCount(); }
  336. #else /* _WIN32 */
  337. static int clock_time(void)
  338. {
  339. struct timeval tv;
  340. struct timezone tz;
  341. gettimeofday(&tv, &tz);
  342. return tv.tv_sec * 1000 + tv.tv_usec / 1000;
  343. }
  344. #endif /* _WIN32 */
  345. static int timer_expired(struct timer *t)
  346. { return (int)(clock_time() - t->start) >= (int)t->interval; }
  347. static void timer_set(struct timer *t, int interval)
  348. { t->interval = interval; t->start = clock_time(); }
  349. /*---------------------------------------------------------------------------*/