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