diff --git a/.github/build-canary-v3 b/.github/build-canary-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/build-canary-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/build-nest-v3 b/.github/build-nest-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/build-nest-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/release-canary-v3 b/.github/release-canary-v3 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.github/release-canary-v3 @@ -0,0 +1 @@ +1 diff --git a/.github/release-nest-v3 b/.github/release-nest-v3 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/.github/release-nest-v3 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/.github/workflows/build-canaryv3.yml b/.github/workflows/build-canaryv3.yml new file mode 100644 index 0000000..bff3d86 --- /dev/null +++ b/.github/workflows/build-canaryv3.yml @@ -0,0 +1,37 @@ +name: PikaOS Package Build Only (Canary) (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/build-canary-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:canaryv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh diff --git a/.github/workflows/build-nestv3.yml b/.github/workflows/build-nestv3.yml new file mode 100644 index 0000000..3c9c34d --- /dev/null +++ b/.github/workflows/build-nestv3.yml @@ -0,0 +1,37 @@ +name: PikaOS Package Build Only (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/build-nest-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:nestv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh diff --git a/.github/workflows/release-canaryv3.yml b/.github/workflows/release-canaryv3.yml new file mode 100644 index 0000000..436c8ff --- /dev/null +++ b/.github/workflows/release-canaryv3.yml @@ -0,0 +1,40 @@ +name: PikaOS Package Build & Release (Canary) (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/release-canary-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:canaryv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh + + - name: Release Package + run: ./release.sh diff --git a/.github/workflows/release-nestv3.yml b/.github/workflows/release-nestv3.yml new file mode 100644 index 0000000..ed42bb7 --- /dev/null +++ b/.github/workflows/release-nestv3.yml @@ -0,0 +1,40 @@ +name: PikaOS Package Build & Release (amd64-v3) + +on: + push: + branches: + - main + paths: + - '.github/release-nest-v3' + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/pikaos-linux/pikaos-builder:nestv3 + volumes: + - /proc:/proc + options: --privileged -it + + steps: + - uses: actions/checkout@v3 + + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.KNOWN_HOSTS }} + if_key_exists: replace + + - name: Update APT Cache + run: apt-get update -y + + - name: Set Build Config + run: cp -vf ./pika-build-config/amd64-v3.sh ./pika-build-config.sh + + - name: Build Package + run: ./main.sh + + - name: Release Package + run: ./release.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index bec37cd..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: PikaOS Package Release - -on: - workflow_dispatch - -jobs: - build: - runs-on: self-hosted - container: - image: ghcr.io/pikaos-linux/pika-package-container:latest - volumes: - - /proc:/proc - options: --privileged -it - - steps: - - uses: actions/checkout@v3 - - - name: Import GPG key - id: import_gpg - uses: crazy-max/ghaction-import-gpg@v5 - with: - gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.PASSPHRASE }} - - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.SSH_KEY }} - name: id_rsa - known_hosts: ${{ secrets.KNOWN_HOSTS }} - if_key_exists: replace - - - name: Update apt cache - run: apt-get update -y - - - name: Build Package - run: ./main.sh - - - name: Release Package - run: ./release.sh - - - name: Purge cache - uses: strrife/cloudflare-chunked-purge-action@master - env: - # Zone is required by both authentication methods - CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }} - - CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} - PURGE_URLS: ${{ vars.PURGE_URLS }} - diff --git a/LICENSE.md b/LICENSE.md index 125ed9b..16ff30b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ -MIT License +MIT License (With DPKG packaging compatibility) -Copyright (c) 2023 PikaOS +Copyright (c) 2024 PikaOS Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +19,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Notes: +The files covered by this license are any files and directories in the root of this repository (including but not limited to: `main.sh`, `release.sh`, and `.github`), with the exception of the `debian` directory and its contents if `debian/copyright` exists, and declares any files or directories as a different LICENSE/COPYRIGHT. diff --git a/debian/changelog b/debian/changelog index 923e83f..07878aa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libfprint (1:1.94.7.git-101pika1) pika; urgency=medium + + * more egismoc support + new release + pika os 4 port + + -- Marco Trevisan (TreviƱo) Wed, 23 Aug 2023 01:11:51 +0200 + libfprint (1:1.94.7-100pika1) pikauwu; urgency=medium * New upstream release diff --git a/debian/libfprint-2-2.install b/debian/libfprint-2-2.install index be3d230..b67ae7d 100644 --- a/debian/libfprint-2-2.install +++ b/debian/libfprint-2-2.install @@ -1,3 +1,4 @@ lib/udev/hwdb.d/ lib/udev/rules.d/ usr/lib/${DEB_HOST_MULTIARCH}/libfprint-[0-9].so.* +egismoc-1c7a-0582.py usr/bin/ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/egismoc-1c7a-0582.py b/egismoc-1c7a-0582.py new file mode 100755 index 0000000..f729844 --- /dev/null +++ b/egismoc-1c7a-0582.py @@ -0,0 +1,436 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +EGIS 1C7A:0582 Match-on-Chip fingerprint reader driver PoC. + +Usage: + ARGV0 -h | --help + ARGV0 info + ARGV0 verify + ARGV0 enroll + ARGV0 wipe + +Options: +-h, --help Show help + +Commands: +info Get device info +verify Verify finger +enroll Enroll a new finger +wipe Wipe all enrolled fingers +""" + +import sys +from docopt import docopt + +import usb.core +import usb.util + +import re +import secrets +import struct + +# find our device +dev = usb.core.find(idVendor=0x1c7a, idProduct=0x0582) +if dev is None: + raise ValueError('Device not found') + +# USB URB Device IDs for 1c7a:0582 device +USB_BULK_OUT = 0x02 +USB_BULK_IN = 0x81 +USB_INTERRUPT_IN = 0x83 + + +# Reading/Writing to the bulk in/out seems to have this kind of payload structure: +# 1) 8 bytes hard-coded prefix depending on out vs in (see below) +# 2) 2 bytes which are a kind of "check bytes" for the payload +# 3) hardcoded "00 00 00" +# 4) 2 bytes that seem to be some kind of type/subtype for different processes/events +# 5) then it seems to vary a bit more with additional payload data depending on different type of events; either some kind of payload directly or more "type" kind of stuff either before or after the payload + +# TODO: Probably better/easier/nicer to move to the struct package for building payloads, but for now we will do it all super-hard-coded with bytes... + +# Bytes prefix for every payload sent to and from device +WRITE_PREFIX = b'EGIS\x00\x00\x00\x01' +READ_PREFIX = b'SIGE\x00\x00\x00\x01' + +# Derive the 2 "check bytes" for write payloads +# 32-bit big-endian sum of all 16-bit words (including check bytes) MOD 0xFFFF should be 0 +def get_check_bytes(payload): + full_payload = WRITE_PREFIX + b'\x00\x00\x00\x00\x00' + payload + full_payload_h_be_ints = [] + # Combine every 2 bytes (32 bits) into 2-byte big-endian "words" + # Example: '0x53 0x49' should become '0x4953' + i = 0 + while i < len(full_payload): + if i+1 >= len(full_payload): + full_payload_h_be_ints.append(struct.unpack('>H', full_payload[i].to_bytes(length=1) + b'\x00')[0]) + else: + full_payload_h_be_ints.append(struct.unpack('>H', full_payload[i:i+2])[0]) + i += 2 + # we can derive the "first occurence" of possible check bytes as `0xFFFF - (sum_of_32bit_words % 0xFFFF)` + # and then pack it so it is split into 2 individual bytes and in the right order + return struct.pack('>H', 0xFFFF - (sum(full_payload_h_be_ints) % 0xFFFF)) + +# Standard method to make it easier to write to USB_BULK_OUT +# This method will create and send a "full" payload which looks like this: +# E G I S 00 00 00 01 {cb1} {cb2} 00 00 00 {payload} +# (where cb1 and cb2 are some check bytes generated by the get_check_bytes() method and payload is what is passed via the parameter) +def write(payload): + full_payload = WRITE_PREFIX + get_check_bytes(payload) + b'\x00\x00\x00' + payload + print(f"Sending: {full_payload}") + return dev.write(USB_BULK_OUT, full_payload) + +# Standard method to make it easier to read from USB_BULK_IN +def read(length=4096, timeout=5000): + return dev.read(USB_BULK_IN, length, timeout) + +# Standard method to make it easier to read from USB_INTERRUPT_IN (wait for response from fingerprint on physical device) +def wait_for_finger(length=64, timeout=10000): # 10s default timeout to put finger on sensor + return dev.read(USB_INTERRUPT_IN, length, timeout) + + +### Setup ### + +# required every time before the device is used; the Windows driver seems to work that the driver is always running in the background so this setup is only done once on Windows startup + +# Put the identifier for each fingerprint in an array in case it is helpful later? We can also use len(fingers_enrolled) to see how many fingerprints are enrolled +fingers_enrolled = [] + +def setup(): + # Reset the device in case it was not closed out properly last time + dev.reset() + + # set the active configuration. With no arguments, the first + # configuration will be the active one + dev.set_configuration(1) + + # get an endpoint instance + cfg = dev.get_active_configuration() + intf = cfg[(0,0)] + + ## Initialization sequence (attempting to match from traces) ## + + # First send some control transfers + print(dev.ctrl_transfer(0xc0, 32, 0x0000, 4, 16).tobytes()) + print(dev.ctrl_transfer(0xc0, 32, 0x0000, 4, 40).tobytes()) + print(dev.ctrl_transfer(0x80, 0, 0x0000, 0, 2).tobytes()) + print(dev.ctrl_transfer(0x80, 0, 0x0000, 0, 2).tobytes()) + print(dev.ctrl_transfer(0xc0, 82, 0x0000, 0, 8).tobytes()) + + cfg = dev.get_active_configuration() + intf = cfg[(0,0)] + + # Then send a series of "initilization" packets + write(b'\x07\x50\x7f\x00\x00\x00\x00\x0c') + print(f"Read: {read().tobytes()}") + + write(b'\x07\x50\x43\x00\x00\x00\x00\x04') + print(f"Read: {read().tobytes()}") + + write(b'\x07\x50\x07\x00\x02\x00\x00\x1d') + # this response seems to have slightly different payload if there are fingers regd already vs not? not sure logic, but for now we can instead use the next sequence instead for this detection? + print(f"Read: {read().tobytes()}") + + write(b'\x07\x50\x19\x04\x00\x00\x01\x40') + # This response seems to return a payload with an identifier/signature of all fingers registered, so we can use it to know if we are ready to verify or not and provide other info + # The payload portion of this packet (listed as #5 in description above) seems to be composed like this: + # - 32 bytes per finger + # - seem to be some kind of identifier/hash of the finger? + # - The 32 byte block sequences can come in different order when fingers are added/removed? + # - then 2 bytes "90 00" + + fingers_enrolled_response = read() + print(f"Read: {fingers_enrolled_response.tobytes()}") + + if len(fingers_enrolled_response) >= 48: + for i in range(int((len(fingers_enrolled_response) - 16) / 32)): + fingers_enrolled.append(fingers_enrolled_response[14+(i*32):14+(i*32)+32]) + + print(f"Device initialization is complete. Number of fingers already enrolled: {len(fingers_enrolled)}") + +# Payload that needs to be built for multiple methods below (both enroll and verify) +def fingers_enrolled_payload(): + # This payload needs to be built based on existing registered fingerprints and seems to be used in multiple different processes + + # ("num fingers regd * 20" + "9") # TODO based on this logic does it mean only max 7 fingers per user? + payload = (((len(fingers_enrolled) + 1) * 0x20) + 0x09).to_bytes() + # then 50 17 03 00 00 00 + payload += b'\x50\x17\x03\x00\x00\x00' + # then ("num fingers regd * 20") # TODO based on this logic does it mean only max 7 fingers per user? + payload += ((len(fingers_enrolled) + 1) * 0x20).to_bytes() + # Then hard-coded 32 x 00s + payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' + payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' + payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' + payload += b'\x00\x00\x00\x00\x00\x00\x00\x00' + # Then each of finger identifiers already registered + for f in reversed(fingers_enrolled): #TODO not sure if reversed is required? but seems to be so in trace anyway + payload += f + # then 00 40 + payload += b'\x00\x40' + + return payload + + +### End Setup ### + + + +# Different methods for performing different operations: info, enroll, verify, and wipe + +### INFO ### + +def info(): + print("Device information:") + print(dev) + print(f"Number of fingers enrolled: {len(fingers_enrolled)}") + + + +### ENROLL ### + +# Sensor read response checks during enrollment + +# TODO: In the Windows driver they seem to be able to tell difference between Move Lower / Higher / Left / Right but could not find any difference in payload for each of these conditions (it was all the same payload pattern for these "not in center" conditions) +# also as a "hack" python has trouble if the byte is 0x0a because it interprets this as a linebreak (char 0a) and will sometimes not match this as "any character" (".") so will use a hack (.|\n) as the placeholder to pick up just in case the byte is 0x0a +FINGER_NOT_YET_ENROLLED_REGEX = re.compile(READ_PREFIX + b'\xd5(.|\n)\x00\x00\x00\x02\x90(.|\n)') +NOT_IN_CENTER_REGEX = re.compile(READ_PREFIX + b'(.|\n)\xd0\x00\x00\x00\x04(.|\n)\x0a\x64\x91') +SENSOR_DIRTY_REGEX = re.compile(READ_PREFIX + b'\x00(.|\n)\x00\x00\x00\x02\x64(.|\n)') +PARTIAL_READ_SUCCESS_REGEX = re.compile(READ_PREFIX + b'(.|\n)\x61\x00\x00\x00\x04(.|\n)\x0a\x90\x00') # Maybe we don't need to care about this one? also some bytes seem to increment/decrement based on which valid_read this is, we could use this if we did not want to keep track of read number in our code + +def enroll(): + + # In the trace it seems like the Windows driver actually kicks off a read to the INTERRUPT IN in a background thread much earlier in the process and it always "stays on" in the background + # Then you can sort of control what will be done using different commands to the BULK OUT and read results using the BULK IN + # But here as a temporary test, we will instead just have a single thread where we kick off the read of the sensor (the INTERRUPT IN) only when we need it instead of this read always running/waiting in the background + + # Setup to read a print + + write(b'\x04\x50\x1a\x00\x00') + print(f"Read: {read().tobytes()}") + + write(b'\x04\x50\x17\x01\x00') + print(f"Read: {read().tobytes()}") + + print("Waiting for fingerprint on sensor. Please touch the power button...") + wait_for_finger() + print("Fingerprint detected!") + + write(b'\x04\x50\x17\x02\x00') + print(f"Read: {read().tobytes()}") + + # Next payload from existing registered fingerprints + write(fingers_enrolled_payload()) + + # this packet returns specific hard-coded bytes if this is a good new finger to add, or some kind of longer payload if it is not + new_finger_response = read() + print(f"Read: {new_finger_response.tobytes()}") + if not FINGER_NOT_YET_ENROLLED_REGEX.fullmatch(new_finger_response): + raise ValueError('That fingerprint is already enrolled. Try a different finger.') + + # TODO: if this result then it is considered a bad packet payload from fingers_enrolled_payload() (bad CRC etc?)? + #b'\xf5\x8c\x00\x00\x00\x02\x6f\xe1' + + # TODO: skip this for now and see if it works without it? otherwise wonder if this might have something to do with associated the current current user to these enrollments? + ## If first time a new finger is being enrolled for this device session, thenn some kind of client info is exchanged? not sure but will just copy what was in the trace for now... + #if len(fingers_enrolled) == 0: + # # TODO is this next payload hardcoded or same every time? + # payload = b'\x6b\x50\x57' + # payload += b'\x01\x00\x00\x00\x62\x20\x76\x6b' + # payload += b'\x30\x62\xb2\xc1\xc7\x18\x55\xeb' + # payload += b'\x3a\xf3\x90\x70\xf9\x7f\x8b\x8b' + # payload += b'\xda\x93\x10\xa8\x87\xc8\x75\xa0' + # payload += b'\xe9\x73\xbf\x70\x31\x8f\x04\xc5' + # payload += b'\x00\xbf\x04\x77\xe8\xd3\x09\x0f' + # payload += b'\x5f\xa3\x1a\x20\x23\x60\x43\x78' + # payload += b'\x39\xf5\x73\x49\xbd\x25\xe2\x7c' + # payload += b'\xe3\xa9\xc9\x07\x18\xb5\x62\xb1' + # payload += b'\xa5\x2b\x83\x97\x84\x17\x43\x4b' + # payload += b'\xd1\x1a\x47\x5b\x94\x82\xa7\x3e' + # payload += b'\x7d\x26\xc0\xab\x38\x57\x59\xb3' + # payload += b'\x0d\x86\x59\x00\xb3\x6d\xe6\x00' + # payload += b'\x00' + # write(payload) + # print(f"Read: {read().tobytes()}") # TODO this response is quite larger and seems to have a bit of Windows / device / cert info with it? + + write(b'\x07\x50\x16\x01\x00\x00\x00\x20') + print(f"Read: {read().tobytes()}") # TODO this response has some kind of identifier in it? not sure what it is used for (is this the user ID?) + + write(b'\x04\x50\x1a\x00\x00') + print(f"Read: {read().tobytes()}") + + write(b'\x04\x50\x16\x02\x01') + print(f"Read: {read().tobytes()}") + + read_prompt = "Began registration of a new fingerprint. Please touch the sensor again..." + + valid_reads = 0 + while valid_reads < 10: + print(read_prompt) + wait_for_finger() + print("Fingerprint detected!") + + write(b'\x07\x50\x16\x02\x02\x00\x00\x02') + + finger_read_response = read() + print(f"Read: {finger_read_response.tobytes()}") + + # Check read success; retry failures without incrementing valid_reads + is_valid = False + if NOT_IN_CENTER_REGEX.fullmatch(finger_read_response): + read_prompt = "Finger was not centered on the sensor. Please try to move to the center and try again..." #TODO how does Windows driver tell difference between higher/lower/left/right? + elif SENSOR_DIRTY_REGEX.fullmatch(finger_read_response): + read_prompt = "The sensor appears dirty or cannot recognize you, please try again..." + elif not PARTIAL_READ_SUCCESS_REGEX.fullmatch(finger_read_response): + raise ValueError("Unknown response from partial read.") + else: + is_valid = True + + if valid_reads < 9 or not is_valid: + write(b'\x04\x50\x1a\x00\x00') + print(f"Read: {read().tobytes()}") + + write(b'\x04\x50\x16\x02\x01') + print(f"Read: {read().tobytes()}") + else: + write(b'\x07\x50\x16\x05\x00\x00\x00\x20') + print(f"Read: {read().tobytes()}") + + # Build new enrolled fingerprint identifier payload + # first is a hardcoded string of bytes + payload = b'\x27\x50\x16\x03\x00\x00\x00\x20' + # Then the rest is actually the "identifier" for the fingerprint (which is sent back from device later) - how should this be generated? + # I think the driver is actually creating and assigning these here? Not sure how it is generated, maybe start with just generating a new 32 byte token? + new_finger_id = secrets.token_bytes(32) + payload += new_finger_id + write(payload) + print(f"Read: {read().tobytes()}") + # TODO: In theory this should be "per user" e.g. per os.getlogin() and then that these IDs per user are saved somewhere like a file or database? + fingers_enrolled.append(new_finger_id) + print("Success! New fingerprint added.") + + if is_valid: + valid_reads += 1 + read_prompt = f"Great job! Please touch the sensor again... ({valid_reads}/10)" + + + +### VERIFY ### + +# Sensor read response checks during verification + +# "Verified" seems to be a prefix of 2 bytes (?), then "00 00 00 00 42", then 32 bytes of "something?" (is this the user ID?), then a 32 byte id of the fingerprint which was verified, then "90 00" +# So I assume you can figure out which user is associated with the verification based on which users has this fingerprint ID +VERIFIED_REGEX = re.compile(READ_PREFIX + b'(.|\n)(.|\n)\x00\x00\x00\x42(.|\n){32}((.|\n){32})\x90\x00') # TODO: think this is still not working exactly right to capture the ID? + +# If "not verified," instead of matching the above pattern it seems like it is usually a hardcoded byte string on the same read? +NOT_VERIFIED_REGEX = re.compile(READ_PREFIX + b'\xd5\x69\x00\x00\x00\x02\x90\x04') + +def verify(): + + if len(fingers_enrolled) == 0: + raise ValueError('No fingers are enrolled!') + + # setup to read a print - in the trace this seems to be done after the read is kicked off on INTERRUPT IN but is it ok to do single-threaded like this? + write(b'\x04\x50\x17\x01\x01') + print(f"Read: {read().tobytes()}") + + print("Waiting for fingerprint on sensor. Please touch the power button...") + wait_for_finger() + print("Fingerprint detected!") + + # post-processing of some kind? + write(b'\x04\x50\x17\x02\x00') + print(f"Read: {read().tobytes()}") + + # Next payload from existing registered fingerprints + write(fingers_enrolled_payload()) + verify_finger_payload = read() + print(f"Read: {verify_finger_payload.tobytes()}") + + result = False + mtch = VERIFIED_REGEX.match(verify_finger_payload) + if not mtch: + result = False + print('Your fingerprint could not be recognized. Please try a different finger.') + else: + result = True + print("Matched fingerprint! TODO: capture ID and match vs user") + + ## TODO: This capture / check may not be needed but also seems to be an issue with the regex/logic -- comment out for now... + #finger_id = mtch.group(4) # needs to be 4 due to workaround with (.|\n), so the finger ID is the 5th capture group we can get from the match + #if finger_id in fingers_enrolled: + # print(f"Found and matched fingerprint {finger_id} !") + #else: + # raise ValueError('Fingerprint seemed to be valid but could not be found in existing list of enrolled prints; what happened???') + + write(b'\x04\x50\x1a\x00\x00') + print(f"Read: {read().tobytes()}") + + write(b'\x04\x50\x17\x01\x01') + print(f"Read: {read().tobytes()}") + + write(b'\x04\x50\x04\x01\x00') + print(f"Read: {read().tobytes()}") + + # Get a response from the sensor a second time - why? + print("Getting second response from sensor...") + second_finger_response = wait_for_finger() + # TODO: does this need to be checked for valid/invalid ? + print("Second response detected!") + print(f"Read: {second_finger_response.tobytes()}") + + write(b'\x04\x50\x04\x02\x00') + print(f"Read: {read().tobytes()}") + + return result + + + +### WIPE ### + +def wipe(): + + if len(fingers_enrolled) == 0: + raise ValueError('No fingers are enrolled!') + + # Build the full payload for the packet + + # ("num fingers regd * 20" + "7") + payload = (((len(fingers_enrolled)) * 0x20) + 0x07).to_bytes() + # then 50 18 04 00 00 00 + payload += b'\x50\x18\x04\x00\x00\x00' + # then ("num fingers regd * 20") + payload += ((len(fingers_enrolled)) * 0x20).to_bytes() + # Then the fingerprint IDs from above + for f in reversed(fingers_enrolled): + payload += f + + write(payload) + print(f"Read: {read().tobytes()}") # TODO check for success? is this always READ_PREFIX + '\xd5\x6d\x00\x00\x00\x02\x90\x00' when success? + + # TODO: how to know it was "valid" or not? The read() above seems to throw a device I/O exception if there was a failure, maybe that is good enough? + print("Wipe successful!") + return True + + + +def main(args): + if args["info"]: + setup() + info() + if args["enroll"]: + setup() + enroll() + if args["verify"]: + setup() + verify() + if args["wipe"]: + setup() + wipe() + +if __name__ == '__main__': + args = docopt(__doc__.replace("ARGV0", sys.argv[0])) + main(args) diff --git a/main.sh b/main.sh index 8ff021a..68e1341 100755 --- a/main.sh +++ b/main.sh @@ -1,11 +1,19 @@ #! /bin/bash -DEBIAN_FRONTEND=noninteractive +set -e + +VERSION="1.94.7.git" + +source ./pika-build-config.sh + +echo "$PIKA_BUILD_ARCH" > pika-build-arch # Clone Upstream -git clone -b egismoc-0587 https://gitlab.freedesktop.org/thameruddin/libfprint ./libfprintd-2-2 -cp -rvf ./debian ./libfprintd-2-2/ -cd ./libfprintd-2-2/ +#git clone https://gitlab.freedesktop.org/libfprint/libfprint -b v"$VERSION" +git clone https://gitlab.freedesktop.org/libfprint/libfprint +cp -rvf ./debian ./libfprint/ +cp -rvf ./egismoc-1c7a-0582.py ./libfprint/ +cd ./libfprint/ for i in $(cat ../patches/series | grep -v '^#') ; do echo "Applying Patch: $i" && patch -Np1 -i ../patches/$i || bash -c "echo "Applying Patch $i Failed!" && exit 2"; done @@ -13,6 +21,7 @@ for i in $(cat ../patches/series | grep -v '^#') ; do echo "Applying Patch: $i" apt-get build-dep ./ -y # Build package +LOGNAME=root dh_make --createorig -y -l -p libfprint_"$VERSION" || echo "dh-make: Ignoring Last Error" dpkg-buildpackage --no-sign # Move the debs to output diff --git a/patches/469.patch b/patches/469.patch index 8dbb098..fa28bc0 100644 --- a/patches/469.patch +++ b/patches/469.patch @@ -1,7 +1,7 @@ -From d6334a7a4ce57092d426c5016aed286c61b3bffb Mon Sep 17 00:00:00 2001 +From d8eb9844f52b72cbb7ffed51d8c1dc49f68356c6 Mon Sep 17 00:00:00 2001 From: Tamer Hassan Date: Sat, 9 Mar 2024 12:20:15 +0400 -Subject: [PATCH] egismoc: add 0587 support (also supports 0586 but missing +Subject: [PATCH 1/2] egismoc: add 0587 support (also supports 0586 but missing device file) --- @@ -18,7 +18,7 @@ Subject: [PATCH] egismoc: add 0587 support (also supports 0586 but missing create mode 100644 tests/egismoc-0587/device diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb -index 74ac65b0..d8a112b0 100644 +index 8e0e6eab..f9842ae4 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -79,6 +79,7 @@ usb:v1C7Ap0571* @@ -1000,3 +1000,841 @@ index dc3b70e2..7a7afc03 100644 -- GitLab + +From 4c50d9b6a0cb45681e1cd08411df477772f6821c Mon Sep 17 00:00:00 2001 +From: Tamer Hassan +Date: Wed, 17 Apr 2024 16:09:36 +0400 +Subject: [PATCH 2/2] egismoc: add 0586 support + +--- + data/autosuspend.hwdb | 1 + + libfprint/drivers/egismoc/egismoc.c | 6 +- + tests/egismoc-0586/custom.pcapng | Bin 0 -> 92980 bytes + tests/egismoc-0586/custom.py | 156 +++++++++++++++++ + tests/egismoc-0586/device | 255 ++++++++++++++++++++++++++++ + tests/meson.build | 1 + + 6 files changed, 414 insertions(+), 5 deletions(-) + create mode 100644 tests/egismoc-0586/custom.pcapng + create mode 100755 tests/egismoc-0586/custom.py + create mode 100644 tests/egismoc-0586/device + +diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb +index f9842ae4..0e4c30ef 100644 +--- a/data/autosuspend.hwdb ++++ b/data/autosuspend.hwdb +@@ -79,6 +79,7 @@ usb:v1C7Ap0571* + + # Supported by libfprint driver egismoc + usb:v1C7Ap0582* ++usb:v1C7Ap0586* + usb:v1C7Ap0587* + usb:v1C7Ap05A1* + ID_AUTOSUSPEND=1 +diff --git a/libfprint/drivers/egismoc/egismoc.c b/libfprint/drivers/egismoc/egismoc.c +index 97997a2d..de5ec765 100644 +--- a/libfprint/drivers/egismoc/egismoc.c ++++ b/libfprint/drivers/egismoc/egismoc.c +@@ -51,11 +51,7 @@ G_DEFINE_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FP_TYPE_DEVICE); + + static const FpIdEntry egismoc_id_table[] = { + { .vid = 0x1c7a, .pid = 0x0582, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 }, +- /* +- * 0x0586 is supported in the same way as 0587 per user report, but missing submission of device file to be included +- * +- * { .vid = 0x1c7a, .pid = 0x0586, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, +- */ ++ { .vid = 0x1c7a, .pid = 0x0586, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, + { .vid = 0x1c7a, .pid = 0x0587, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 | EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 }, + { .vid = 0x1c7a, .pid = 0x05a1, .driver_data = EGISMOC_DRIVER_CHECK_PREFIX_TYPE2 }, + { .vid = 0, .pid = 0, .driver_data = 0 } +diff --git a/tests/egismoc-0586/custom.pcapng b/tests/egismoc-0586/custom.pcapng +new file mode 100644 +index 0000000000000000000000000000000000000000..f43dc6f4d1c06801e083c37eaab5e45729444ebe +GIT binary patch +literal 92980 +zcmd6Q2bdK__Whe73?NYi)KwT=6j%j@j4MI|0)n8kWJT~11xZ5^QNn;Ah^PaK=(@nD +zm=)1A$H9Pzl0}RtAcBITE~Z_9|Giyxs`}mO7rOWDe}46S^-}k{r*55Fr*2pG>sO&t +zr3z~zkw}j==bV~B&&PGij5LWfX*PLec9-Gfvb&C(JbX<3o(-})j-NQZe(xR)vPZRQ +z*rZv@oZRgCQ%BKGy?S+O-l%B<`hKZM^+@+o1HENz+zssn}+1IGod4q?J9(>jCVcCNxsjUrN +z!(^M=$!)xl>St7)qEeB}NLFOZq#;!^J#=t%PkPT4_B*=v8%AJ%71`&)7Clzo&)f%X|HO<)=5cE3b*J4}rtgr9 +z%u>_}#*#jrLzoVI&J~!_r^mEU +z$UeA(ed$x{^OR5jOYTZHNbcu_)DHHOqk>C**;1v-m&QB#K*vy&UUF(mv86(kjwC(kzl2=@{t~X-%)1(-#<1#^tfU`1uxL +zeT;y^w&yF~3}$)WTD(N*BWZvCImPXF%w+ow+roamHj2aI7Gpp0v)`#Y +z??W7aA{qUJTIg4n(YtcU +zw~>8r2v^vber_iFT-ZJ^+z;@eKjdg<+tP=#n^_;6;h(e|?xX1=I)?Y*Ub4^a=L-AM +zhuvhK3pZ6a*L`mu^oQ|$=+Gj$4|6&x9~2VxCnq}*yrY*IBT6s1k494E!aRII_AjE> +zT;usrr?axfkLne@@D^>qLS^;gNP1$NYxO>ibAQXDqx;Z%uE6P4DwB1_=-6JuVU4aq +z`*4j{I(6yVi?(H)w2L0Ia%b^=6{#|wjDanL>(J+J)iKaf;TnCb+C_Pl2$M{h{4&aK +zsIXsuYE|5RuTrzwuQt%n0c4;3@)sX6B>3^pJy)K0bo2n)g)924qq1-MZDm*0{y)(^ +zT#FTLzgO2TooHJ|iyib>>Aaj4IgMJ9S;c*<{atXs@S0DS5$D6cYsg6DFKQ7GUuLF|v*lNuGmdCqP +zbKLf9zT)WUDfFHz;!0|0m9dJ)7PJr7cm*0dh2yGT?#VPBBiYHqi}vC6NR9oD+U3ya +zZjHlpLuwb7L~Y{jQL55@m4U_WOP(*)ey7TF9)nR~o=aU68Iwz|xuRdC#&+y~0`Kmc +zuj;-M4`RIT7)#qT3rr5_)zxphMaJ}@&0K*ed2BG?gg|p)D!O{_#gQ=%V|sAqT)3NX +zxzunU<3B`cljP}>?#1m(o=zR8{0hTUzaGhXT1NJ{Fh{BIbbLJRm^u7d9zC%?UY<(# +zNzT)S!_&{x1%&HRTihCFJk20nE@60T-`CnNI;Pj$M9wmMM0lQlAp5!0Hm;Ykd79V>spI>KuVTvAN~2k;C2$HM!h!gg%oTO}IK3M^f{=9CaIDIYLZG&09D+ +z7)RM%spCrL*5YF)KaOS*rbFAf0#kDII$?4N!%@#GJod46EymH3G2uBX6SW+fwNa7# +zcghRK(Fg9lwAjo`QP+Dl4>lWY{c-l_ag=dG%(2X*3CbTwe-BSaYctg+QF`K-E~LtZ +z=h^uUtB>J>HLkGVQQ40y&}*#o|5KTFkF?X(eM1k@K8|G?)~1b3YGq{e+FokKO_B@g +zYur9>jP`3>-RMZsPq8|J_Vqhj*`1eD)sZ?nM!8%3V)R?WbZ8q_U?N6=`7L2`2@|7z +zn|SOWhEMAF6!!ZbS3cQJcH!)U;51KfVNOrJwYt@MZ67*vljiiJQ`8rjL&KxBZjOJ! +zsnii~4qT<6`k1NoU9Qei_@lHQc$?`Un2)zx(LP+`6=?lyf2KVeri>(N6YmkFD(8cWORW8(Z{WA$5%%9dUHR?b$z(JEj}5=` +z@Tl!e9>3GcQOZ1yV$6cl953vTH@B*BxVp`O55S*T9(P=){NWrYQJw=HBVB06tlXMB +zA7oQyJlT({2-l&{-KudVPs6UK9jGM2`_)|NM;?$30q-J_KDtgv( +z<5_-ykomT +zw6)P+*hieg{-~=s8q_%R9cwynFduwy1?^lqqe=liVQhOb?;qm96mvH)_l~mH?D%4G +zgQ#HcPNw-eLt7i395HVLGw*8ar?T$<7^RQpyghT6wJ-B4*ne%b@+pj-oKV!-=n3pg +zo-TezwV%hc8Pi@D1W$=Hzs*_?M)Rl!~dVkRZ6?&x65p2`W?7Z^JkS;{Srp=?<{5QI}_lSItGAWA68I)r5dMR +z{aw(#OD$P{MP-*)9%sH|O=!t>SMxLDYJP+M7PBY{pH!X?pGI97Y;7c&=HE-0 +z4z-^vFeOjB36qPjkM0)r5Tz%nCtqD=?MH^fr&ylq4i3LIa_e)B(G%F0JZ*YawLeuo +zNu>E@Ua%bH4~9Rn9Cdm<7)Qx8|4PDisBgIfQ*yMDFu8=`sMd?tzCBkK%Td3g@Ei@S +z(cW+b`;w#Or&V#=pDIU*G{1GN_NKpphddwS=-S#nt#y?9!#7M(DO4eFPaf7hfJpV +zMTF^cL_C;t1p0a{U=|T37hfOUE$SgkPg3(|y=d*@oQmXW*39sE{*qc389jl0=n2}t +z36o10j_Uu( +z+IKF8PfGJ)zy6ouIa+m%=GB#?a|cN +zb~;{niQx&DQcpUzvwmvxzE4s5RBHZ|>pk|xd3yBY6erm6Se{1bPx+? +z=3}i&UrTX|&huMcP;z;G{wuw$=8N@5w9jdm{yhIJ!gZ*R+$z=}fjgIQxrEXDvtG0I +zv5t#4h5da__&mSk#eI$D!#?5^_DgM2=Og0kuh9H0uI87CtNF)!SWK*~Lr;MD`WAJL +zA}*%eqp55D)JytWp2T`SFk5Z4erof+Pf_|*YW|m(dhCny@UUOFE!cS%ujW^Jysyy{ +zdEN!~H#Sl^a5^-<@CnNi&QnQ_qB-f!^J}iQeF#iwK79O;FuC~V9_|+P5Tz%{(WECm +z_QiR2*l*S}e4gK#9r-yt*YYdZ}xhnRi`E^-s#K0>%$!f5`SQr14!UJ$3SpLwD3 +zD~#s<*A&N;Tvh(Dl*?6IOt(d; +z=lTC6OozVXRke`*JM(MCabPq{>ku%|ABY;}4EowC1C~X0B2GI4-JvV0^deu^Xi~ +zJVzE%<-%(_H;)}*G~eWdLi0~pt&UqtLi7I^Kf-7}jzgr5Okbmpb69&5=`XJTOXXV4 +z-gzTzoQgaj{r&;ra!G{A+oM!vZnA4aar-h(2R$9^c+`8{&bG26?btT$SLbli{+{|? +z6miG4(EQ)uBS%VSRT;L}|60wlTlKqxtdlq|d{u{i*6nqCEeGQI;dDapbtD_8}F`fAQ1NM)P4G=Wt>FywlVgKuKu+i=U6SnlH}bn)cPM +znD#vXTf*i3bZeaa@oK{55=Qf@e_`#*Yl`95?RAu2spgMf&EI|9Sflx{FXMFZ%l3HM +zcdQA`-{|J~<>Kb~E2djaoU4<1Qv20l>jufzcGeT7%MtMaOsV-75hfR3AKfkLAxckj +zZmu@N+Lz}C!P8>xQ>r|9HUGI!#~RIteaTbH(<-|SOjS=3X@1|&EJx5U$|7i3;e +zruh>H(;*MI0#kDIqxLZjM|-{Y#d&scH1zcF*J|$m+{2N!FF6`qI~YfaG{3_x+h1n= +zP2~A+5FT^Hed;Z#X#Vg|$6L+ETAR-EZ_{f#4}7ewmW1YKem>r4KGxC@qiA1;PlNqN +zHr +zO4NCr`HnTA`M|aavgZy^RtNDj_PcWJf`;w=|U#j+}L-R}TwH!sT +zZlKSP!Ji9v2jeK2<~Jlvhx(Q)Fy&YpOPE~3aJ1NC->wD3j-{>Ir!e#K%g#i@5$sEj +z{?k^iAEwGtBF%rI{Y2AWz(bx7j=pQB))O7K*X%aq89l3g=v7 +zG(Vn}bZellDUPeZY8aaJ{P*2FzkJ+0|J8FXrd&6GPe=cvt}Bj<>Go*qn!l4U9r})2 +z<7j@ZbF81*yzf(#K9!pPPD77_aM=->2&|qxrCpwH?IV(k>NT +zqa~sFCtN(uXg=~)8KW0>t&sM$oe6}?{pnUS7S$Rg`sFFYyF~;uh9JWT+Oc#SM!JeYB4d-NUk3ESzTil7t`(0 +z)HVNR!gT06T!AU|CDN11)pnqsOPCDZ(agy~RIxdKyibPi#13Bys} +z{?>jx&A+p6uys-|N0NwEdBkdU>qgV{P&-+{uCkR^x6($qT)Z5KaSgL_QAx5 +zj)^F3;yk91Di=P7d+doBR`a!esrhXmuk7w$5}N84*6?CAfA(tCFNrX@39f*-szI>(cO_lVCrpR>m#bWZ1Lk_dNF|~9 +zhrgI(HDBD{7VSHHkGgItotl5fmvc3DnYtE<@q97e7Nwr& +zuOLi^zQYxmQcqS8CYMBbygf=)YJS#AYaioX>PgmsVAof9HNVaubCc>xoflO5c|4mj +zO|MN6JSEcntw$|Km}4YITVD*uQ8LXxN|+8cmMbtNM@I>hOBjyY9QW84`4h%co0r3L +zG>z=%Qro!Nyh0s|!TvO|&m~ok5^4UX$UNI$@JHwQz}xhS^2c#e?SqLA9TQR7#4RhN +z%7yd%rGMOLG~f86*Yjb2=?m&Qs*=$BF~@GSns4$fH{Xt+ePdoy*I}ho^A{7YLw&?G +zUYsr_TrOcW|Fh%PempJt?B!tBmw7e6%hL<2=I>GW%$1toWt>`@imSguOV+!ZUn#EU +zKmUxyL@Y@?c|NZ~pld>s<@ueTU0``K$20194Dr!j*Ds^7~d@-<_h)49Mik1-)uEstnFaT?Aol>s!Brh_t&`DXudgz +zEsjO^Z&hns>D2tuHE*_YigVivw=Mc*^fuKmVKje#EsuS1ZV>kOZx6Nx>DBx%iWb?i +zZO%u;<|bcEuk1!bs+#|ftNE4VYW|aNTFgiv-0LcKetvS6TCa+WsbdrCaLF|PW5RUk +zJ6wS&bI6YglS>#q>HfC0?{tDsYCRu3b)Ow<4KB}Zjne$QGjB0^0{c==@^-2Ar>ZB3 +zG=E;*TWnmI`;o9qf8GU)&De&0#3}4|dR|>$7FU0Du7Ka+u5&fNN?gq!-OgebwK +zU|(|dL{Tt~66N{tbg}+m>>|%c%*|h`{Bhh~vk$4{`Lj#kVKg81rRKM%d&P1o3C(}3 +z%pFNJfAlQ1wv|rJe}`}#@|kNq&7V)WT*7Gnq_WmN=2{$&g4bsG!PX$Xn*Zj&JB;ST +zzKqiwcB=NLOY^&5X)%!xNj<6fvGOS{rrV>bujdaTOozV16_`>_&Lm7OVf3VQ)Y^~4 +znVa9zKBYQ0d-MFYuiRlYAND0rvs(tc4lq%k|IVu(j<#VAi=CI}w@UA|nx7M5gC=CDQ!zr!6)8h2P0a +z&3|`k_-i%ie0`VEeAt(o-yy25bt?(YZ+YM@qxp8-J2pmtp?kpbZ(`D^`I8CPp%`?l +zSc8NnHy@( +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 +z8Xu{C3X^}An^4@oah>K +zkUTH`B-k1_bIOHz{-av8v3c~GE9^_2Uzw)Pv%-o-e`V^X+3XcVZ>ySOJz?D4a&QSf32$R0au^;oKa +z!Kg5g3ujm!Wqt=;?zZoL%cD2{+c~qEom=&}70KhWua&>4&aKIE^wzU%AIlsaeefG$ +zatSlHev@zQ}gzQ3Lvi*>$H?XODT;aaSSu_)KJyd3>9 +z$HUR3xSyZO(a~SeeH)CUWI6hv8*Cp&G^QM9wGW2pXy4r8_9aIT9171-w|SN$+kUlX +zfj+D7T`-Ok<>-5EwEmcw(`)3wyYGADk0XM?#D|WFC~cQH`iV^&n;aeXk)z+EbM&4y +zls_erqc?8e*yu=6?fZTj=DB7O?HgY!Sd1pi(cdCW?n|z~M2rHn^vU64bU{mNKT;Mx +zsq;9nzy6f)F?w}DW1}Om4;_L1M<)e~QK=)|HS&j+G&VW9b2|J{Is&}?Qn6%~ +zF=zk(xJISJ?Uo0$Tk<^T8r4sU+DtC_&iVW9u=a~;pkHEnKI_`>JWrpIV|a#r$@44G +zVE12^zq#<{W>aS6nA|K9fj_Z4KRZO-e>v6MEYgKKkA54&YdYCf8BZS5-cPs=ea;oQ +zlBd0d%Oy;1R)2PJ`6pWw|38hCcJpx=Xh`Rp*k6Z`)b*?C#rZHQe&-tIdwQQ)sUP_s;u&e4Ar6 +z)x>2ik8L(7f76wl)j4SU*v6`h*%p0#C1G+2lbdZgWbI=tOAae+3C3Zb+Zx5;w-+@r +zV-5BthkrR&-G8SleUEFgA~=+q>g8y4woHQz%^ICfKuv09hP{5+ +zJ0H~l15{d;r{)gv%w&d=UMt +zxP8Pa>__SM(LvVMykmQBwRU!Fi}?Wd_dad&OJ6yMh3ftOb!*u0!f^Y;^`L+x;@ +z@lnmoz}!!mT*7eF^BQX(F(AiM&syQ<sBV31k<_cVB32?6=TrOcWzvWbq{QFo4AYR=*K(4b9CEc4@cU*Mf~g{*sM7jON3>)cl_3sywMA^89*Rdsxjc +z-}s&3o;6tbzrV4{v(l;gml3W*oyoOG<4P?#B}es37|rjp-P(6ff?u(5n$!(w|1e=1o;0T9 +zX(VBC@%7Q&q8_63BsD*yv9<4%flskKt<^sHFT9XZ-H))JM;~$FbEoxgxzuXDwl8_w +ze=vP{{@hzFNBG}inL}OpT`-Q4Y5pUG>5#u}J%IncjMe;6gvligM^D~n?c?6Z$n&8Q +z-`i+D?h%X_Mf-LgNdLWwZz5cW+~x{gXbEu3f5RKY<@vieTKo1s`$|h-fAF`# +z?s@Cg{PIouTFu{sapu~O!2V4=lo;_i^BrqitFX4S)z$pcaW(&`W)>6cgp#W!xxwx= +zkxcVHAWVna&lQ-Gr{09g#n(r7i+YIClhpign|th=a}=>WUH!N4_nIiXq_5F@dp;tT +zryEzR_NODy-+8;`2daAuuIJiwKiT7>=G^YVG5E0W=@6-t_74 +z9A*4qIf8v?KJ3q~80=UqQJ%lyM<0LmdOq;FRSuu$&zv^EXuk1Bujj-5G5U`aE+vuY +zAGvOT(R_>v8KaYzrT^Z1 +zAKZ1Ii3^;klH+Oj@L(Jz)BHyW)1kiQ3QWmSgAw65>eciy`i|k^La&#=IOo +z_n?;}WnXgC^37lzCDQ!<4|)8#wd3>d+J?~lj|h+bQGM!mtoy(*;ubx2qx6Pjx{xXt +zKDNxx8Dccw(e`DYU)Wv!4!0yUzfO}OM)M;}wg37)s^D{F5B2-zbgu1mAY6y~h%0bq +zoW4Q0T*7Gn43GVyd*D~BmK@c7h0**M?i*^xHtZu#SL)-p-=@3GceT)04|Uui&)@87 +ze%ZL1|M&YXCSnPC0?bclr1w8ao+M0%+;A)6SKluHn1>0Ii?5GrllvH@N2&Q6J@$+4 +ziscFRE6ogcJm%Gtx(^ODnh*Pur(yZjFe>TL{56EvlWcK~{0VcInwR0v5Bb43N~Za5 +z5~f3NT!D$P1k6&id1E+^zVg_|ypKE|_Un@Uu$n)h%rMK5$tzU;1pD`Vq1vA+M~O85 +zl(NH2e<2URdOq+j`%?MiC|L8DisoA +zJUyj-N|h(C=0Ek;)k*c_#}%smsp?6hbGWPDwj5c1V#m|>D}!;AO!K!ArbB(p6_}Ev +zYK7rBI&+=1Z!}Wh!yFnmgzSga{E>eiZ8(B`IhGzi8jPbvn!mR0XwzSaX{q@c$CW?a +zr{0o^=D&T>riKM +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 + +literal 0 +HcmV?d00001 + +diff --git a/tests/egismoc-0586/custom.py b/tests/egismoc-0586/custom.py +new file mode 100755 +index 00000000..3a662380 +--- /dev/null ++++ b/tests/egismoc-0586/custom.py +@@ -0,0 +1,156 @@ ++#!/usr/bin/python3 ++ ++import traceback ++import sys ++import time ++import gi ++ ++gi.require_version('FPrint', '2.0') ++from gi.repository import FPrint, GLib ++ ++# Exit with error on any exception, included those happening in async callbacks ++sys.excepthook = lambda *args: (traceback.print_exception(*args), sys.exit(1)) ++ ++ctx = GLib.main_context_default() ++ ++c = FPrint.Context() ++c.enumerate() ++devices = c.get_devices() ++ ++d = devices[0] ++del devices ++ ++d.open_sync() ++ ++assert d.get_driver() == "egismoc" ++assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) ++assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) ++assert d.has_feature(FPrint.DeviceFeature.VERIFY) ++assert d.has_feature(FPrint.DeviceFeature.DUPLICATES_CHECK) ++assert d.has_feature(FPrint.DeviceFeature.STORAGE) ++assert d.has_feature(FPrint.DeviceFeature.STORAGE_LIST) ++assert d.has_feature(FPrint.DeviceFeature.STORAGE_DELETE) ++assert d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) ++ ++def enroll_progress(*args): ++ print("finger status: ", d.get_finger_status()) ++ print('enroll progress: ' + str(args)) ++ ++def identify_done(dev, res): ++ global identified ++ identified = True ++ identify_match, identify_print = dev.identify_finish(res) ++ print('indentification_done: ', identify_match, identify_print) ++ assert identify_match.equal(identify_print) ++ ++# Beginning with list and clear assumes you begin with >0 prints enrolled before capturing ++ ++print("listing - device should have prints") ++stored = d.list_prints_sync() ++assert len(stored) > 0 ++del stored ++ ++print("clear device storage") ++d.clear_storage_sync() ++print("clear done") ++ ++print("listing - device should be empty") ++stored = d.list_prints_sync() ++assert len(stored) == 0 ++del stored ++ ++print("enrolling") ++template = FPrint.Print.new(d) ++template.set_finger(FPrint.Finger.LEFT_INDEX) ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++p1 = d.enroll_sync(template, None, enroll_progress, None) ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++print("enroll done") ++del template ++ ++print("listing - device should have 1 print") ++stored = d.list_prints_sync() ++assert len(stored) == 1 ++assert stored[0].equal(p1) ++ ++print("verifying") ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++verify_res, verify_print = d.verify_sync(p1) ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++print("verify done") ++assert verify_res == True ++ ++identified = False ++deserialized_prints = [] ++for p in stored: ++ deserialized_prints.append(FPrint.Print.deserialize(p.serialize())) ++ assert deserialized_prints[-1].equal(p) ++del stored ++ ++print('async identifying') ++d.identify(deserialized_prints, callback=identify_done) ++del deserialized_prints ++ ++while not identified: ++ ctx.iteration(True) ++ ++print("try to enroll duplicate") ++template = FPrint.Print.new(d) ++template.set_finger(FPrint.Finger.RIGHT_INDEX) ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++try: ++ d.enroll_sync(template, None, enroll_progress, None) ++except GLib.Error as error: ++ assert error.matches(FPrint.DeviceError.quark(), ++ FPrint.DeviceError.DATA_DUPLICATE) ++except Exception as exc: ++ raise ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++print("duplicate enroll attempt done") ++ ++print("listing - device should still only have 1 print") ++stored = d.list_prints_sync() ++assert len(stored) == 1 ++assert stored[0].equal(p1) ++del stored ++ ++print("enroll new finger") ++template = FPrint.Print.new(d) ++template.set_finger(FPrint.Finger.RIGHT_INDEX) ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++p2 = d.enroll_sync(template, None, enroll_progress, None) ++assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE ++print("enroll new finger done") ++del template ++ ++print("listing - device should have 2 prints") ++stored = d.list_prints_sync() ++assert len(stored) == 2 ++assert (stored[0].equal(p1) and stored[1].equal(p2)) or (stored[0].equal(p2) and stored[1].equal(p1)) ++del stored ++ ++print("deleting first print") ++d.delete_print_sync(p1) ++print("delete done") ++del p1 ++ ++print("listing - device should only have second print") ++stored = d.list_prints_sync() ++assert len(stored) == 1 ++assert stored[0].equal(p2) ++del stored ++del p2 ++ ++print("clear device storage") ++d.clear_storage_sync() ++print("clear done") ++ ++print("listing - device should be empty") ++stored = d.list_prints_sync() ++assert len(stored) == 0 ++del stored ++ ++d.close_sync() ++ ++del d ++del c +diff --git a/tests/egismoc-0586/device b/tests/egismoc-0586/device +new file mode 100644 +index 00000000..fb41aeea +--- /dev/null ++++ b/tests/egismoc-0586/device +@@ -0,0 +1,255 @@ ++P: /devices/pci0000:00/0000:00:14.0/usb1/1-7 ++N: bus/usb/001/021=12010002FF0000407A1C860556620102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005 ++E: BUSNUM=001 ++E: DEVNAME=/dev/bus/usb/001/021 ++E: DEVNUM=021 ++E: DEVTYPE=usb_device ++E: DRIVER=usb ++E: ID_AUTOSUSPEND=1 ++E: ID_BUS=usb ++E: ID_MODEL=ETU905A88-E ++E: ID_MODEL_ENC=ETU905A88-E ++E: ID_MODEL_ID=0586 ++E: ID_PATH=pci-0000:00:14.0-usb-0:7 ++E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_7 ++E: ID_PATH_WITH_USB_REVISION=pci-0000:00:14.0-usbv2-0:7 ++E: ID_PERSIST=0 ++E: ID_REVISION=6256 ++E: ID_SERIAL=EGIS_ETU905A88-E_0A5743PCU834 ++E: ID_SERIAL_SHORT=0A5743PCU834 ++E: ID_USB_INTERFACES=:ff0000: ++E: ID_USB_MODEL=ETU905A88-E ++E: ID_USB_MODEL_ENC=ETU905A88-E ++E: ID_USB_MODEL_ID=0586 ++E: ID_USB_REVISION=6256 ++E: ID_USB_SERIAL=EGIS_ETU905A88-E_0A5743PCU834 ++E: ID_USB_SERIAL_SHORT=0A5743PCU834 ++E: ID_USB_VENDOR=EGIS ++E: ID_USB_VENDOR_ENC=EGIS ++E: ID_USB_VENDOR_ID=1c7a ++E: ID_VENDOR=EGIS ++E: ID_VENDOR_ENC=EGIS ++E: ID_VENDOR_FROM_DATABASE=LighTuning Technology Inc. ++E: ID_VENDOR_ID=1c7a ++E: MAJOR=189 ++E: MINOR=20 ++E: PRODUCT=1c7a/586/6256 ++E: SUBSYSTEM=usb ++E: TYPE=255/0/0 ++A: authorized=1\n ++A: avoid_reset_quirk=0\n ++A: bConfigurationValue=1\n ++A: bDeviceClass=ff\n ++A: bDeviceProtocol=00\n ++A: bDeviceSubClass=00\n ++A: bMaxPacketSize0=64\n ++A: bMaxPower=100mA\n ++A: bNumConfigurations=1\n ++A: bNumInterfaces= 1\n ++A: bcdDevice=6256\n ++A: bmAttributes=a0\n ++A: busnum=1\n ++A: configuration= ++H: descriptors=12010002FF0000407A1C860556620102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005 ++A: dev=189:20\n ++A: devnum=21\n ++A: devpath=7\n ++L: driver=../../../../../bus/usb/drivers/usb ++L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4e/device:4f/device:56 ++A: idProduct=0586\n ++A: idVendor=1c7a\n ++A: ltm_capable=no\n ++A: manufacturer=EGIS\n ++A: maxchild=0\n ++A: physical_location/dock=no\n ++A: physical_location/horizontal_position=center\n ++A: physical_location/lid=no\n ++A: physical_location/panel=front\n ++A: physical_location/vertical_position=center\n ++L: port=../1-0:1.0/usb1-port7 ++A: power/active_duration=12644\n ++A: power/autosuspend=2\n ++A: power/autosuspend_delay_ms=2000\n ++A: power/connected_duration=230907\n ++A: power/control=auto\n ++A: power/level=auto\n ++A: power/persist=0\n ++A: power/runtime_active_time=12929\n ++A: power/runtime_status=active\n ++A: power/runtime_suspended_time=217715\n ++A: power/wakeup=disabled\n ++A: power/wakeup_abort_count=\n ++A: power/wakeup_active=\n ++A: power/wakeup_active_count=\n ++A: power/wakeup_count=\n ++A: power/wakeup_expire_count=\n ++A: power/wakeup_last_time_ms=\n ++A: power/wakeup_max_time_ms=\n ++A: power/wakeup_total_time_ms=\n ++A: product=ETU905A88-E\n ++A: quirks=0x0\n ++A: removable=fixed\n ++A: rx_lanes=1\n ++A: serial=0A5743PCU834\n ++A: speed=480\n ++A: tx_lanes=1\n ++A: urbnum=18\n ++A: version= 2.00\n ++ ++P: /devices/pci0000:00/0000:00:14.0/usb1 ++N: bus/usb/001/001=12010002090001406B1D020008060302010109021900010100E0000904000001090000000705810304000C ++E: BUSNUM=001 ++E: CURRENT_TAGS=:seat: ++E: DEVNAME=/dev/bus/usb/001/001 ++E: DEVNUM=001 ++E: DEVTYPE=usb_device ++E: DRIVER=usb ++E: ID_AUTOSUSPEND=1 ++E: ID_BUS=usb ++E: ID_FOR_SEAT=usb-pci-0000_00_14_0 ++E: ID_MODEL=xHCI_Host_Controller ++E: ID_MODEL_ENC=xHCI\x20Host\x20Controller ++E: ID_MODEL_FROM_DATABASE=2.0 root hub ++E: ID_MODEL_ID=0002 ++E: ID_PATH=pci-0000:00:14.0 ++E: ID_PATH_TAG=pci-0000_00_14_0 ++E: ID_REVISION=0608 ++E: ID_SERIAL=Linux_6.8.5-arch1-1_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 ++E: ID_SERIAL_SHORT=0000:00:14.0 ++E: ID_USB_INTERFACES=:090000: ++E: ID_USB_MODEL=xHCI_Host_Controller ++E: ID_USB_MODEL_ENC=xHCI\x20Host\x20Controller ++E: ID_USB_MODEL_ID=0002 ++E: ID_USB_REVISION=0608 ++E: ID_USB_SERIAL=Linux_6.8.5-arch1-1_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 ++E: ID_USB_SERIAL_SHORT=0000:00:14.0 ++E: ID_USB_VENDOR=Linux_6.8.5-arch1-1_xhci-hcd ++E: ID_USB_VENDOR_ENC=Linux\x206.8.5-arch1-1\x20xhci-hcd ++E: ID_USB_VENDOR_ID=1d6b ++E: ID_VENDOR=Linux_6.8.5-arch1-1_xhci-hcd ++E: ID_VENDOR_ENC=Linux\x206.8.5-arch1-1\x20xhci-hcd ++E: ID_VENDOR_FROM_DATABASE=Linux Foundation ++E: ID_VENDOR_ID=1d6b ++E: MAJOR=189 ++E: MINOR=0 ++E: PRODUCT=1d6b/2/608 ++E: SUBSYSTEM=usb ++E: TAGS=:seat: ++E: TYPE=9/0/1 ++A: authorized=1\n ++A: authorized_default=1\n ++A: avoid_reset_quirk=0\n ++A: bConfigurationValue=1\n ++A: bDeviceClass=09\n ++A: bDeviceProtocol=01\n ++A: bDeviceSubClass=00\n ++A: bMaxPacketSize0=64\n ++A: bMaxPower=0mA\n ++A: bNumConfigurations=1\n ++A: bNumInterfaces= 1\n ++A: bcdDevice=0608\n ++A: bmAttributes=e0\n ++A: busnum=1\n ++A: configuration= ++H: descriptors=12010002090001406B1D020008060302010109021900010100E0000904000001090000000705810304000C ++A: dev=189:0\n ++A: devnum=1\n ++A: devpath=0\n ++L: driver=../../../../bus/usb/drivers/usb ++L: firmware_node=../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4e/device:4f ++A: idProduct=0002\n ++A: idVendor=1d6b\n ++A: interface_authorized_default=1\n ++A: ltm_capable=no\n ++A: manufacturer=Linux 6.8.5-arch1-1 xhci-hcd\n ++A: maxchild=12\n ++A: power/active_duration=73066477\n ++A: power/autosuspend=0\n ++A: power/autosuspend_delay_ms=0\n ++A: power/connected_duration=73071614\n ++A: power/control=auto\n ++A: power/level=auto\n ++A: power/runtime_active_time=73070027\n ++A: power/runtime_status=active\n ++A: power/runtime_suspended_time=0\n ++A: power/wakeup=disabled\n ++A: power/wakeup_abort_count=\n ++A: power/wakeup_active=\n ++A: power/wakeup_active_count=\n ++A: power/wakeup_count=\n ++A: power/wakeup_expire_count=\n ++A: power/wakeup_last_time_ms=\n ++A: power/wakeup_max_time_ms=\n ++A: power/wakeup_total_time_ms=\n ++A: product=xHCI Host Controller\n ++A: quirks=0x0\n ++A: removable=unknown\n ++A: rx_lanes=1\n ++A: serial=0000:00:14.0\n ++A: speed=480\n ++A: tx_lanes=1\n ++A: urbnum=1111\n ++A: version= 2.00\n ++ ++P: /devices/pci0000:00/0000:00:14.0 ++E: DRIVER=xhci_hcd ++E: ID_AUTOSUSPEND=1 ++E: ID_MODEL_FROM_DATABASE=Alder Lake PCH USB 3.2 xHCI Host Controller ++E: ID_PATH=pci-0000:00:14.0 ++E: ID_PATH_TAG=pci-0000_00_14_0 ++E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller ++E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI ++E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller ++E: ID_VENDOR_FROM_DATABASE=Intel Corporation ++E: MODALIAS=pci:v00008086d000051EDsv00001043sd0000201Fbc0Csc03i30 ++E: PCI_CLASS=C0330 ++E: PCI_ID=8086:51ED ++E: PCI_SLOT_NAME=0000:00:14.0 ++E: PCI_SUBSYS_ID=1043:201F ++E: SUBSYSTEM=pci ++A: ari_enabled=0\n ++A: broken_parity_status=0\n ++A: class=0x0c0330\n ++H: config=8680ED51060490020130030C000080000400220560000000000000000000000000000000000000000000000043101F20000000007000000000000000FF010000FD0134A089C27F8000000000000000003F6DD80F000000000000000000000000316000000000000000000000000000000180C2C10800000000000000000000000590B7001805E0FE000000000000000009B014F01000400100000000C10A080000080E00001800008F50020000010000090000018680C00009001014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B50F110112000000 ++A: consistent_dma_mask_bits=64\n ++A: d3cold_allowed=1\n ++A: device=0x51ed\n ++A: dma_mask_bits=64\n ++L: driver=../../../bus/pci/drivers/xhci_hcd ++A: driver_override=(null)\n ++A: enable=1\n ++L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4e ++A: irq=143\n ++A: local_cpulist=0-15\n ++A: local_cpus=ffff\n ++A: modalias=pci:v00008086d000051EDsv00001043sd0000201Fbc0Csc03i30\n ++A: msi_bus=1\n ++A: msi_irqs/143=msi\n ++A: msi_irqs/144=msi\n ++A: msi_irqs/145=msi\n ++A: msi_irqs/146=msi\n ++A: msi_irqs/147=msi\n ++A: msi_irqs/148=msi\n ++A: msi_irqs/149=msi\n ++A: msi_irqs/150=msi\n ++A: numa_node=-1\n ++A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 8 9 2112 9\nxHCI ring segments 43 53 4096 53\nbuffer-2048 0 16 2048 8\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 128 32 1\n ++A: power/control=auto\n ++A: power/runtime_active_time=73070690\n ++A: power/runtime_status=active\n ++A: power/runtime_suspended_time=0\n ++A: power/wakeup=enabled\n ++A: power/wakeup_abort_count=0\n ++A: power/wakeup_active=0\n ++A: power/wakeup_active_count=0\n ++A: power/wakeup_count=0\n ++A: power/wakeup_expire_count=0\n ++A: power/wakeup_last_time_ms=0\n ++A: power/wakeup_max_time_ms=0\n ++A: power/wakeup_total_time_ms=0\n ++A: power_state=D0\n ++A: resource=0x0000006005220000 0x000000600522ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n ++A: revision=0x01\n ++A: subsystem_device=0x201f\n ++A: subsystem_vendor=0x1043\n ++A: vendor=0x8086\n +diff --git a/tests/meson.build b/tests/meson.build +index 7a7afc03..2b9691ee 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -53,6 +53,7 @@ drivers_tests = [ + 'egis0570', + 'egismoc', + 'egismoc-05a1', ++ 'egismoc-0586', + 'egismoc-0587', + 'fpcmoc', + 'realtek', +-- +GitLab + diff --git a/pika-build-config/amd64-v3.sh b/pika-build-config/amd64-v3.sh new file mode 100755 index 0000000..10285b4 --- /dev/null +++ b/pika-build-config/amd64-v3.sh @@ -0,0 +1,10 @@ +#! /bin/bash +export PIKA_BUILD_ARCH="amd64-v3" +export DEBIAN_FRONTEND="noninteractive" +export DEB_BUILD_MAINT_OPTIONS="optimize=+lto -march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CPPFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_CXXFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_LDFLAGS_MAINT_APPEND="-march=x86-64-v3 -O3 -flto -fuse-linker-plugin -falign-functions=32" +export DEB_BUILD_OPTIONS="nocheck notest terse" +export DPKG_GENSYMBOLS_CHECK_LEVEL=0 diff --git a/pika-build-config/i386.sh b/pika-build-config/i386.sh new file mode 100755 index 0000000..7629d66 --- /dev/null +++ b/pika-build-config/i386.sh @@ -0,0 +1,5 @@ +#! /bin/bash +export PIKA_BUILD_ARCH="i386" +export DEBIAN_FRONTEND="noninteractive" +export DEB_BUILD_OPTIONS="nocheck notest terse" +export DPKG_GENSYMBOLS_CHECK_LEVEL=0 diff --git a/release.sh b/release.sh index 21ee3e7..660f48f 100755 --- a/release.sh +++ b/release.sh @@ -1,8 +1,2 @@ # send debs to server -rsync -azP --include './' --include '*.deb' --exclude '*' ./output/ ferreo@direct.pika-os.com:/srv/www/incoming/ - -# add debs to repo -ssh ferreo@direct.pika-os.com 'aptly repo add -force-replace -remove-files pikauwu-main /srv/www/incoming/' - -# publish the repo -ssh ferreo@direct.pika-os.com 'aptly publish update -batch -skip-contents -force-overwrite pikauwu filesystem:pikarepo:' +rsync -azP --include './' --include '*.deb' --exclude '*' ./output/ ferreo@direct.pika-os.com:/srv/www/cockatiel-incoming/