/** * Process a switch that has transitioned states. All debouncing * is fully completed prior to this call. * * The transition is first latched, so that polling the switch level * will return the new state. At the same time, IRQ-level scanning * is restarted, so that further transitions can be detected. * * Second, if the transition requires scheduling, a task is started * to handle it. Eligibility depends on whether or not the switch * is declared as an edge switch (meaning it is scheduled on both * types of transitions) and whether or not it is an opto (i.e. * do we schedule open-to-closed or closed-to-open?) * * The switch is also known not to be in the debounce queue prior to this * function being called. */ void switch_transitioned (const U8 sw) { /* Latch the transition. sw_logical is still an open/closed level. * By clearing the stable/unstable bits, IRQ will begin scanning * for new transitions at this point. */ disable_irq (); bit_toggle (sw_logical, sw); bit_off (sw_stable, sw); bit_off (sw_unstable, sw); enable_irq (); /* See if the transition requires scheduling. It does if the switch is declared 'edge' (it schedules when becoming active or inactive), otherwise only becoming active. Because most switches are not edge, check for the active state first. */ if (switch_poll_logical (sw) || bit_test (mach_edge_switches, sw)) { #ifdef CONFIG_BPT /* One extra condition : do not schedule any switches when the system is paused */ if (db_paused != 0) return; #endif /* Start a task to process the switch event. This task may sleep if necessary, but it should be as fast as possible and push long-lived operations into separate background tasks. It is possible for more than one instance of a task to exist for the same switch, if valid debounced transitions occur quickly. */ task_pid_t tp = task_create_gid (GID_SW_HANDLER, switch_sched_task); task_set_arg (tp, sw); } }
/** * This task runs during a switch stress test. During a game, * it randomly invokes switch handlers as if the switches had actually * been activated by the pinball. */ void switch_stress_task (void) { U8 sw; const switch_info_t *swinfo; task_pid_t tp; /* Delay a few seconds before starting the simulation. This allows time for the Start Button to be used to add players, instead of simulating endball. */ task_sleep_sec (3); task_create_peer (switch_stress_flipper_task); for (;;) { task_sleep (TIME_100MS); if (in_test) continue; /* Choose a switch at random. Skip certain well-known switches that are * never to be activated. */ sw = random_scaled (NUM_SWITCHES); #ifdef SW_ALWAYS_CLOSED if (sw == SW_ALWAYS_CLOSED) continue; #endif #ifdef MACHINE_OUTHOLE_SWITCH if (sw == MACHINE_OUTHOLE_SWITCH) continue; #endif /* Lookup the switch properties. Skip switches which aren't normally * activated on the playfield. For switches in a ball container, * simulate device entry there, otherwise simulate a switch event. */ swinfo = switch_lookup (sw); if (SW_HAS_DEVICE (swinfo)) { device_t *dev = device_entry (SW_GET_DEVICE (swinfo)); if (trough_dev_p (dev)) { /* Don't always trigger the trough device. The probability of a ball drain is treated as proportional to the number of balls in play. So in big multiballs, we allow this to happen more frequently. We need to do this occasionally so that multiball modes will eventually end, else nothing else gets tested. */ if (random () < CONFIG_STRESS_DRAIN_PROB * live_balls) switch_stress_drain (); } else if (dev->max_count < dev->size) { /* Don't throw an enter event if the device thinks it is full: it has "locked" as many balls as it can hold. The device code will throw a fatal if it sees this, which should never happen with real balls. */ dbprintf ("Sim. enter dev %d\n", dev->devno); device_call_op (dev, enter); task_sleep (TIME_1S); } } else if (swinfo->flags & SW_PLAYFIELD) { /* Simulate the switch */ tp = task_create_gid (GID_SW_HANDLER, switch_sched_task); task_set_arg (tp, sw); } } }
task_pid_t timer_start (task_gid_t gid, U16 ticks, task_function_t fn) { task_pid_t tp = task_create_gid (gid, fn); task_set_arg (tp, ticks); return (tp); }