I have a linux box that is hosted at ISP. Couple of times I've managed to lock myself out of it by messing with network settings, and after the latest successful shot at my foot I decided to do something about it. After some websurfing for ideas, this is what came out:

The thing is meant to be connected to mobile phone via handsfree connector (I used an old Nokia 3310 handset). JP1 pin1 goes to microphone wire, pin3 is audio output wire. The phone gets its charging voltage from JP3. JP5 is wired to reset the PC, and CN1 is a male side of disk drive power connector. The phone is configured to auto-answer incoming calls, and does not require PIN code when started.
MT8870D is DTMF detector, when you make a call to the mobile then the call is automatically answered and you can enter DTMF codes that the detector will detect and decode to Q1-Q4 outputs, then it pulses the STD output high. AVR microcontroller that runs off the DTMF decoders clock generator is interrupted, reads the code and reacts accordingly. AVR's PB4 is wired as audio output, so that the device can send back beeps. The reset signal is controlled by small 5V relay, driven by Q1.
The software is quite rudimentary, it recognizes an activation command of 4 digit PIN followed by pound sign (#), and allows chaning PIN by entering a PIN code followed by asterisk (*), a new pin code, another asterisk, re-entry of new pin code finally followed by pound sign. If anything goes wrong, you get audiable beep back. If everything goes fine then the commands are silently obeyed - PIN is changed, or the relay is activated for about 2 seconds.
; DTMF rebooter v1.0
; Atmel AVR 90S2313 microcontroller
;
; Madis Kaal
; mast@nomad.ee
;
; dtmf sequences recognized are
;
; DDDD# - trigger relay on for 2 seconds
; DDDD*NNNN*NNNN# - change pin to NNNN
;
; PIN is kept in 2 first locations of EEPROM as BCD value
; high byte at address 0, low byte at address 1
; for example, setting two first bytes of EEPROM to 0x12,0x34 will
; result in PIN value 1234
;
.include "2313def.inc"
; tmr0 reload value for 5ms interval (actually 5.006ms)
;
.equ tmrloadvalue = 186
; port b pins
;
.equ REL_AH = 0 ; relay output, high activates relay
.equ PULSE = 1 ; debug output, low pulse in DTMF receiver
.equ TMRCLOCK = 2 ; debug output pin, short low puls on every timer interrupt
.equ LED_AL = 3 ; led, on while relay active, flashes when error beep is emitted
.equ SPKROUT = 4 ; audio output pin
; port d pins
;
.equ STD_AH = 2 ; strobe wired to PD2 (INT0)
.equ Q1 = 3 ; dtmf detector output pins
.equ Q2 = 4
.equ Q3 = 5
.equ Q4 = 6
.set GIE = 7 ;interrupt enable bit in sreg
;data segment
;not reserving any space here, but 0x60..0x6f is serial
;data receive buffer
;
.DSEG
.org 0x60 ;variable space begins here
.def pinh = R11
.def pinl = R12
.def beepf = R13
.def tick1s = R14
.def acc = R16
.def beepcnt = R15
.def creload = R17 ;scrambled int TMR0 interrupt handler
.def tick5ms = R18
.def codeh = R19
.def codel = R20
.def entryh = R21
.def entryl = R22
.def dtmfin = R23 ;dtmf value read (4 low bits)
.def state = R24 ;count of dtmf values read since last
; z is used for state machine calls
.CSEG
.org 0
rjmp RESET ; Reset Handler
rjmp EXT_INT0 ; IRQ0 Handler
rjmp EXT_INT1 ; IRQ1 Handler
rjmp TIM_CAPT1 ; Timer1 Capture Handler
rjmp TIM_COMP1 ; Timer1 Compare Handler
rjmp TIM_OVF1 ; Timer1 Overflow Handler
rjmp TIM_OVF0 ; Timer0 Overflow Handler
rjmp UART_RXC ; UART RX Complete Handler
rjmp UART_DRE ; UDR Empty Handler
rjmp UART_TXC ; UART TX Complete Handler
rjmp ANA_COMP ; Analog Comparator Handler
UART_RXC: ; UART RX Complete Handler
EXT_INT1: ; IRQ1 Handler
TIM_CAPT1: ; Timer1 Capture Handler
TIM_COMP1: ; Timer1 Compare Handler
TIM_OVF1: ; Timer1 Overflow Handler
UART_DRE: ; UDR Empty Handler
UART_TXC: ; UART TX Complete Handler
ANA_COMP: ; Analog Comparator Handler
reti
; timer interrupt handler
;
TIM_OVF0:
push R0
in R0,SREG ;save status flags
cbi portb,TMRCLOCK
out tcnt0,creload
dec tick5ms
brne tof1
ldi tick5ms,200
tst tick1s
breq tof1
dec tick1s
brne tof1
; timeout, reset state machine
; also deactivates relay if it was active
clr state
cbi portb,REL_AH
sbi portb,LED_AL
rcall readpin
tof1:
sbi portb,TMRCLOCK
out SREG,R0 ;restore status flags
pop R0
reti
; read dtmf code, return it in dtmfin
; MT8870 outputs decoded values, then rises STD output high
; DTMF to output binay value translation:
; D - 0x0
; 1 - 0x1
; 2 - 0x2
; 3 - 0x3
; ...
; 0 - 0xa
; * - 0xb
; # - 0xc
; A - 0xd
; B - 0xe
; C - 0xf
;
getdtmf:
in dtmfin,pind
lsr dtmfin
lsr dtmfin
lsr dtmfin
andi dtmfin,0x0f
mov acc,dtmfin
ret
; invoked when there is new DTMF digit available
; digit code can be read from pd3..pd6
;
EXT_INT0:
push R0
in R0,SREG
cbi portb,PULSE
tst beepf
brne exitint0
push acc
rcall getdtmf ;read the DTMF code
rcall esmach ;run it through the state machine
pop acc
exitint0:
sbi portb,PULSE
out SREG,R0
pop R0
reti
; entry state machine
; jmp to $100+(state*2)
; entry points have to return with ret
esmach:
rcall clrtimeout
ldi r31,0x01
mov r30,state
ijmp
RESET:
ldi acc,0xdf ;set stack to end of ram
out SPL,acc
; enable watchdog
ldi acc,0x1b
out wdtcr,acc
; set I/O directions
; d1 output
; d0,d2..d6 output
; b0..b4 output
; b5..b7 input
;
ldi acc,0x0e
out portb,acc
ldi acc,0x1f
out ddrb,acc
ldi acc,0x02
out ddrd,acc
ldi tick5ms,200
clr tick1s
clr state
clr beepf
; read pin from eeprom
;
rcall readpin
; set up timer, load initial value, enable overflow interrupts
;
ldi acc,4
out tccr0,acc
ldi creload,tmrloadvalue
out tcnt0,creload
ldi acc,0x02
out timsk,acc ;enable timer 0 overflow interrupts
ldi acc,0x03
out mcucr,acc ;int0 ineterrupt on rising edge
ldi acc,0x40
out gimsk,acc ;enable interrupts on int0
bset GIE ;enable all interrupts
;mainloop does only beeps
;
mainloop:
wdr
tst beepf
breq mainloop
rcall beep
clr beepf
rjmp mainloop ;and loop the loop
; read codeh,codel from eeprom
;
readpin:
clr acc ;start from address 0
out eear,acc ;EEPROM address
sbi eecr,EERE ;EEPROM read strobe
inc acc ;move to next adr
in codeh,eedr ;get the eeprom byte
out eear,acc
sbi eecr,EERE
in codel,eedr
ret
; write one byte to eeprom
; byte must be in eedr
; addr must be in eear
;
eewrite:
ldi acc,4
out eecr,acc
sbi eecr,EEWE
; wait for eeprom write to finish
;
eewait:
wdr
sbic eecr,EEWE
rjmp eewait
ret
; write codeh,codel to eeprom
;
writepin:
bclr GIE
rcall eewait
clr acc
out eear,acc
out eedr,entryh
rcall eewrite
ldi acc,1
out eear,acc
out eedr,entryl
rcall eewrite
bset GIE ;enable all interrupts
ret
; emit 1000 Hz on SPKROUT for 1 sec
; also flash the led twice
;
beep:
cbi portb,LED_AL
rcall b0
rcall b0
cbi portb,LED_AL
rcall b0
b0: ldi acc,250
mov beepcnt,acc
b2: sbi portb,SPKROUT
rcall w500us
cbi portb,SPKROUT
rcall w500us
dec beepcnt
brne b2
sbi portb,LED_AL
ret
; approx 500us wait
w500us:
ldi acc,198
b11:
dec acc
push acc
pop acc
nop
wdr
brne b11
ret
;---------------------------------------------------------------
.org 0x100
rjmp pin1
rjmp pin2
rjmp pin3
rjmp pin4
rjmp poundorstar
rjmp pin1
rjmp pin2
rjmp pin3
rjmp pin4
rjmp star2
rjmp pin1
rjmp pin2
rjmp pin3
rjmp pin4
rjmp pound2
; when bad pin is entered then reset the state
; and make an error beep
badpin:
clr state
inc beepf
ret
; add dtmf code in dtmfin to code accumulator, reset timeout counter
;
adddtmf:
lsl entryl
rol entryh ;*2
lsl entryl
rol entryh ;*4
lsl entryl
rol entryh ;*8
lsl entryl
rol entryh ;*16
or entryl,dtmfin
ret
; check the pin in entryl,entryh against stored PIN
;
chkpin:
cp entryl,codel
brne cp1
cp entryh,codeh
cp1:
ret
; first 4 pin digits are just accumulated
;
pin1:
clr entryl
clr entryh
pin2:
pin3:
pin4:
rcall adddtmf
inc state
ret
; pound after pin terminates input and executes
; star after pin prepares for pin change
; * - 0xb
; # - 0xc
poundorstar:
cpi dtmfin,0xc ; is it #?
breq checkexec ; yes, proceed with command
cpi dtmfin,0xb
brne badpin ; was not * or #, break out
rcall chkpin ; check if the entered pin was correct
brne badpin ; no, will not change
inc state ; correct pin, proceeding with pin change
ret
; check pin, and execute if correct
;
checkexec:
rcall chkpin ; correct pin?
brne badpin ; no, do nothing
; execute command
;
execute:
sbi portb,REL_AH ; activate reset
cbi portb,LED_AL
clr state ; switch to initial state
; reset timeout timer to 2 seconds
;
clrtimeout:
ldi tick5ms,200
ldi acc,2
mov tick1s,acc ; set timer for 2 seconds
ret ; timer interrupt handler will deactivate the signal
; time to get the second asterisk
;
star2:
cpi dtmfin,0xb
brne badpin ; was not '*', break out
mov pinl,entryl
mov pinh,entryh
inc state ; pinh,pinl contain a first pin entry now
ret
; end of pin change, must be pound
;
pound2:
cpi dtmfin,0xc ; # now?
brne badpin ; no, will not change pin
cp pinl,entryl
brne badpin ; entries not the same
cp pinh,entryh
brne badpin
rcall writepin ; write new pin
rcall readpin ; read back to codeh,codel
ret