1 from __future__ import with_statement
2 from pylibusb import lowlevel, highlevel
3 import sys
4
5 startreg_offset = 0x80
6
7 # XXX: according to the biopod kernel driver there's more registers, but they aren't described and I'd prefer only to set the registers that have atleast a bit of a description
8 default_registers = [
9 (0x80, "Control Register 1", 0x00),
10 (0x81, "Control Register 2", 0x00),
11 (0x82, "Excitation Common Controls", 0x00),
12 (0x83, "Detect Control", 0x13), # 0x10 in the kernel driver
13 (0x84, "Reference Resistor", 0x07),
14 (0x85, "Reference Capacitor", 0x7F),
15 (0x86, "Detect Drive", 0x03),
16 (0x87, "Detect Frequency", 0x01),
17 (0x88, "Column Scan Rate", 0x02),
18 (0x89, "Measure Drive", 0x03),
19 (0x8A, "Measure Frequency", 0x05),
20 (0x8B, "Z-Matrix Kernel Size", 0x2F),
21 (0x8C, "Demod Phase 2", 0x7A),
22 (0x8D, "Demod Phase 1", 0x00),
23 (0x8E, "Channel Gain", 0x20),
24 (0x8F, "Channel Bias", 0x22),
25 (0x90, "Carrier Null", 0x00),
26 (0x91, "A/D Reference High", 0x14),
27 (0x92, "A/D Reference Low", 0x03),
28 (0x93, "Start Row", 0x00),
29 (0x94, "End Row", 0x0F),
30 (0x95, "Start Column", 0x00),
31 (0x96, "End Column", 0x7F),
32 (0x97, "Data Format", 0x04),
33 (0x98, "** Was 20 ** Image Data Control", 0x20),
34 (0x99, "LED Control", 0x00),
35 (0x9A, "Status", 0x00),
36 (0x9B, "Challenge Word 1", 0x00),
37 (0x9C, "Challenge Word 2", 0x00),
38 (0x9D, "Challenge Word 3", 0x00),
39 (0x9E, "Challenge Word 4", 0x00),
40 (0x9F, "Challenge Word 5", 0x00),
41 (0xA0, "Bias", 0x00),
42 #(0xA1, None, 0x03),
43 #(0xA2, None, 0x02),
44 #(0xA3, None, 0x00),
45 #(0xA4, None, 0x00),
46 #(0xA5, None, 0x00),
47 #(0xA6, None, 0x00),
48 #(0xA7. None, 0x08),
49 #(0xA8, None, 0x00),
50 #(0xA9, None, 0x40),
51 #(0xAA, None, 0x00),
52 #(0xAB, None, 0x00),
53 #(0xAC, None, 0x00),
54 #(0xAD, None, 0x00),
55 #(0xAE, None, 0x00),
56 #(0xAF, None, 0x00),
57 #(0xB0, None, 0x00),
58 #(0xB1, None, 0x00),
59 #(0xB2, None, 0x00),
60 #(0xB3, None, 0x00),
61 #(0xB4, None, 0x05),
62 #(0xB5, None, 0x00),
63 #(0xB6, None, 0x00),
64 #(0xB7, None, 0x00),
65 #(0xB8, None, 0x00),
66 #(0xB9, None, 0x00),
67 #(0xBA, None, 0x00),
68 #(0xBB, None, 0x22),
69 ]
70
71 class BioPod(object):
72 def __init__(self, device):
73 self.device = device
74 self.ep_in = None
75 self.ep_out = None
76 self.closed = True
77 self.iface_claimed = False
78 assert device.descriptor.idVendor == 0x08ff # 08ff AuthenTec, Inc.
79 # XXX: handle other Product id's
80 assert device.descriptor.idProduct == 0x5731 # 5731 AES3500 TruePrint Sensor
81 self.handle = lowlevel.open(device)
82 self.closed = False
83 # device setup stuff
84 iface = device.config[0].interface[0]
85 self.iface = iface
86 for lowlevel_ep in iface.altsetting[0].endpoint:
87 ep = highlevel.get_endpoint(lowlevel_ep, self.handle)
88 if ep.direction == "in":
89 assert self.ep_in == None, "more than 1 'in' endpoints on the given device"
90 self.ep_in = ep
91 elif ep.direction == "out":
92 assert self.ep_out == None, "more than 1 'out' endpoints on the given device"
93 self.ep_out = ep
94 assert self.ep_in and self.ep_out
95 lowlevel.claim_interface(self.handle, iface.altsetting[0].bInterfaceNumber)
96 self.iface_claimed = True
97 # set the default registers
98 self.ep_out.write("\x87\x01", 1000)
99 #self.ep_out.write("\x89\x03", 1000)
100 #self.ep_out.write("\x8A\x05", 1000)
101 #self.ep_out.write("\x8B\x2F", 1000)
102 #self.ep_out.write("\x8C\x7A", 1000)
103 #self.ep_out.write("\x8E\x20", 1000)
104 #self.ep_out.write("\x8F\x22", 1000)
105 #self.ep_out.write("\x80\x01", 1000)
106 send = []
107 for reg, desc, value in default_registers:
108 send.append("%s%s" % (chr(reg), chr(value)))
109 print repr("".join(send))
110 self.ep_out.write("".join(send), 1000)
111
112 def try_getting_image(self):
113 # XXX: 0x81 0x01 seems to enable continuious scanning where
114 # 0x04 automatically goes back to 0x00 when done scanning once
115 # (0x02 is for showing the registers, and I think it's bit based
116 # to a degree)
117 self.ep_out.write("\x81\x04", 1000)
118 try:
119 res = self.ep_in.read(128*128, 10000)
120 finally:
121 self.ep_out.write("\x81\x00", 1000)
122 return res
123
124 def __del__(self):
125 self.close()
126
127 def close(self):
128 if self.iface_claimed:
129 lowlevel.release_interface(self.handle, self.iface.altsetting[0].bInterfaceNumber)
130 self.iface_claimed = False
131 if self.closed == False:
132 lowlevel.close(self.handle)
133 self.closed = True
134
135 import Image
136 from cStringIO import StringIO
137
138 colours = (0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00)
139
140 def get_image(f, sections=8, columns=128):
141 img = Image.new("L", (sections*16, columns), 255)
142 pdata = img.load()
143 # XXX: after this there's 129 bytes that I'm not using, starting with a 0xDF byte (seems last 120 bytes is the register info)
144 for section in range(sections):
145 header = f.read(1)
146 assert header == chr(0xE0 + section), (section, header, f.tell())
147 for column in range(columns):
148 for index, c in enumerate(f.read(8)):
149 pdata[127-(section*16)-(index*2), column] = colours[ord(c) & 0x0F]
150 pdata[126-(section*16)-(index*2), column] = colours[(ord(c) >> 4) & 0x0F]
151 return img
152
153 import psyco
154 psyco.bind(get_image)
155
156 if __name__ == "__main__":
157 lowlevel.init()
158 lowlevel.find_busses()
159 lowlevel.find_devices()
160 dev = list(lowlevel.get_busses())[-2].devices
161 bp = BioPod(dev)
162 res = bp.try_getting_image()
163 print res[0]
164 img = get_image(StringIO(res[1]))
165 img.show()
166 img.save("fp.png")
167 img.save("fp.jpg")
168