diff --git a/data/autosuspend.hwdb b/data/autosuspend.hwdb index 2b4dc706..44dbbcd3 100644 --- a/data/autosuspend.hwdb +++ b/data/autosuspend.hwdb @@ -158,6 +158,13 @@ usb:v1C7Ap0603* ID_AUTOSUSPEND=1 ID_PERSIST=0 +# Supported by libfprint driver focaltech_moc +usb:v2808p9E48* +usb:v2808pD979* +usb:v2808pA959* + ID_AUTOSUSPEND=1 + ID_PERSIST=0 + # Supported by libfprint driver fpcmoc usb:v10A5pFFE0* usb:v10A5pA305* diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.c b/libfprint/drivers/focaltech_moc/focaltech_moc.c new file mode 100644 index 00000000..190ab436 --- /dev/null +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.c @@ -0,0 +1,1875 @@ +/* + * Copyright (C) 2022 FocalTech Electronics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "focaltech_moc.h" + +#include + +#define FP_COMPONENT "focaltech_moc" + +#include "drivers_api.h" + +G_DEFINE_TYPE (FpiDeviceFocaltechMoc, fpi_device_focaltech_moc, FP_TYPE_DEVICE) + +static const FpIdEntry id_table[] = { + { .vid = 0x2808, .pid = 0x9e48, }, + { .vid = 0x2808, .pid = 0xd979, }, + { .vid = 0x2808, .pid = 0xa959, }, + { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ +}; + +typedef void (*SynCmdMsgCallback) (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error); + +typedef struct +{ + SynCmdMsgCallback callback; +} CommandData; + +typedef struct +{ + uint8_t h; + uint8_t l; +} FpCmdLen; + +typedef struct +{ + uint8_t magic; + FpCmdLen len; +} FpCmdHeader; + +typedef struct +{ + FpCmdHeader header; + uint8_t code; + uint8_t payload[0]; +} FpCmd; + +typedef struct +{ +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) + uint8_t b0; + uint8_t b1; +#else + uint8_t b1; + uint8_t b0; +#endif +} u16_bytes_t; + +typedef union +{ + u16_bytes_t s; + uint16_t v; +} u_u16_bytes_t; + +static inline uint16_t +get_u16_from_u8_lh (uint8_t l, uint8_t h) +{ + u_u16_bytes_t u_u16_bytes; + + u_u16_bytes.v = 0; + u_u16_bytes.s.b0 = l; + u_u16_bytes.s.b1 = h; + + return u_u16_bytes.v; +} + +static inline uint8_t +get_u8_l_from_u16 (uint16_t v) +{ + u_u16_bytes_t u_u16_bytes; + + u_u16_bytes.v = v; + + return u_u16_bytes.s.b0; +} + +static inline uint8_t +get_u8_h_from_u16 (uint16_t v) +{ + u_u16_bytes_t u_u16_bytes; + + u_u16_bytes.v = v; + + return u_u16_bytes.s.b1; +} + +static uint8_t +fp_cmd_bcc (uint8_t *data, uint16_t len) +{ + int i; + uint8_t bcc = 0; + + for (i = 0; i < len; i++) + bcc ^= data[i]; + + return bcc; +} + +static uint8_t * +focaltech_moc_compose_cmd (uint8_t cmd, const uint8_t *data, uint16_t len) +{ + g_autofree char *cmd_buf = NULL; + FpCmd *fp_cmd = NULL; + uint8_t *bcc = NULL; + uint16_t header_len = len + sizeof (*bcc); + + cmd_buf = g_malloc0 (sizeof (FpCmd) + header_len); + + fp_cmd = (FpCmd *) cmd_buf; + + fp_cmd->header.magic = 0x02; + fp_cmd->header.len.l = get_u8_l_from_u16 (header_len); + fp_cmd->header.len.h = get_u8_h_from_u16 (header_len); + fp_cmd->code = cmd; + + if (data != NULL) + memcpy (fp_cmd->payload, data, len); + + bcc = fp_cmd->payload + len; + *bcc = fp_cmd_bcc ((uint8_t *) &fp_cmd->header.len, bcc - (uint8_t *) &fp_cmd->header.len); + + return g_steal_pointer (&cmd_buf); +} + +static int +focaltech_moc_check_cmd (uint8_t *response_buf, uint16_t len) +{ + int ret = -1; + FpCmd *fp_cmd = NULL; + uint8_t *bcc = NULL; + uint16_t header_len; + uint16_t data_len; + + fp_cmd = (FpCmd *) response_buf; + + if (len < sizeof (FpCmd) + sizeof (*bcc)) + return ret; + + if (fp_cmd->header.magic != 0x02) + return ret; + + header_len = get_u16_from_u8_lh (fp_cmd->header.len.l, fp_cmd->header.len.h); + + if (header_len < sizeof (*bcc)) + return ret; + + if ((sizeof (FpCmd) + header_len) > len) + return ret; + + data_len = header_len - sizeof (*bcc); + + bcc = fp_cmd->payload + data_len; + + if (fp_cmd_bcc ((uint8_t *) &fp_cmd->header.len, + bcc - (uint8_t *) &fp_cmd->header.len) != *bcc) + return ret; + + ret = 0; + return ret; +} + +static void +fp_cmd_receive_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer userdata, + GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + CommandData *data = userdata; + int ssm_state = 0; + + if (error) + { + fpi_ssm_mark_failed (transfer->ssm, g_steal_pointer (&error)); + return; + } + + if (data == NULL) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + ssm_state = fpi_ssm_get_cur_state (transfer->ssm); + + /* skip zero length package */ + if (transfer->actual_length == 0) + { + fpi_ssm_jump_to_state (transfer->ssm, ssm_state); + return; + } + + if (focaltech_moc_check_cmd (transfer->buffer, transfer->actual_length) != 0) + { + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + return; + } + + if (data->callback) + data->callback (self, transfer->buffer, transfer->actual_length, NULL); + + fpi_ssm_mark_completed (transfer->ssm); +} + +typedef enum { + FP_CMD_SEND = 0, + FP_CMD_GET, + FP_CMD_NUM_STATES, +} FpCmdState; + +static void +fp_cmd_run_state (FpiSsm *ssm, + FpDevice *device) +{ + FpiUsbTransfer *transfer; + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + switch (fpi_ssm_get_cur_state (ssm)) + { + case FP_CMD_SEND: + if (self->cmd_transfer) + { + self->cmd_transfer->ssm = ssm; + fpi_usb_transfer_submit (g_steal_pointer (&self->cmd_transfer), + FOCALTECH_MOC_CMD_TIMEOUT, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + } + else + { + fpi_ssm_next_state (ssm); + } + + break; + + case FP_CMD_GET: + if (self->cmd_len_in == 0) + { + CommandData *data = fpi_ssm_get_data (ssm); + + if (data->callback) + data->callback (self, NULL, 0, 0); + + fpi_ssm_mark_completed (ssm); + return; + } + + transfer = fpi_usb_transfer_new (device); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, self->bulk_in_ep, self->cmd_len_in); + fpi_usb_transfer_submit (transfer, + self->cmd_cancelable ? 0 : FOCALTECH_MOC_CMD_TIMEOUT, + self->cmd_cancelable ? fpi_device_get_cancellable (device) : NULL, + fp_cmd_receive_cb, + fpi_ssm_get_data (ssm)); + break; + + } + +} + +static void +fp_cmd_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + g_autoptr(GError) local_error = error; + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + CommandData *data = fpi_ssm_get_data (ssm); + + self->cmd_ssm = NULL; + + if (local_error && data->callback) + data->callback (self, NULL, 0, g_steal_pointer (&local_error)); +} + +static void +fp_cmd_ssm_done_data_free (CommandData *data) +{ + g_free (data); +} + +static void +focaltech_moc_get_cmd (FpDevice *device, guint8 *buffer_out, + gsize length_out, gsize length_in, + gboolean can_be_cancelled, + SynCmdMsgCallback callback) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + g_autoptr(FpiUsbTransfer) transfer = NULL; + CommandData *data = g_new0 (CommandData, 1); + + transfer = fpi_usb_transfer_new (device); + transfer->short_is_error = TRUE; + fpi_usb_transfer_fill_bulk_full (transfer, self->bulk_out_ep, buffer_out, + length_out, g_free); + data->callback = callback; + + self->cmd_transfer = g_steal_pointer (&transfer); + self->cmd_len_in = length_in + 1; + self->cmd_cancelable = can_be_cancelled; + + self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), + fp_cmd_run_state, + FP_CMD_NUM_STATES); + + fpi_ssm_set_data (self->cmd_ssm, data, (GDestroyNotify) fp_cmd_ssm_done_data_free); + + fpi_ssm_start (self->cmd_ssm, fp_cmd_ssm_done); +} + +struct UserId +{ + uint8_t uid[32]; +}; + +static void +fprint_set_uid (FpPrint *print, uint8_t *uid, size_t size) +{ + GVariant *var_uid; + GVariant *var_data; + + var_uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, uid, size, 1); + var_data = g_variant_new ("(@ay)", var_uid); + fpi_print_set_type (print, FPI_PRINT_RAW); + fpi_print_set_device_stored (print, TRUE); + g_object_set (print, "fpi-data", var_data, NULL); +} + +enum enroll_states { + ENROLL_RSP_RETRY, + ENROLL_RSP_ENROLL_REPORT, + ENROLL_RSP_ENROLL_OK, + ENROLL_RSP_ENROLL_CANCEL_REPORT, +}; + +static void +enroll_status_report (FpiDeviceFocaltechMoc *self, int enroll_status_id, + int data, GError *error) +{ + FpDevice *device = FP_DEVICE (self); + + switch (enroll_status_id) + { + case ENROLL_RSP_RETRY: + { + fpi_device_enroll_progress (device, self->num_frames, NULL, + fpi_device_retry_new (FP_DEVICE_RETRY_CENTER_FINGER)); + break; + } + + case ENROLL_RSP_ENROLL_REPORT: + { + fpi_device_enroll_progress (device, self->num_frames, NULL, NULL); + break; + } + + case ENROLL_RSP_ENROLL_OK: + { + FpPrint *print = NULL; + fp_info ("Enrollment was successful!"); + fpi_device_get_enroll_data (device, &print); + fpi_device_enroll_complete (device, g_object_ref (print), NULL); + break; + } + + case ENROLL_RSP_ENROLL_CANCEL_REPORT: + { + fpi_device_enroll_complete (device, NULL, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Enrollment failed (%d) (ENROLL_RSP_ENROLL_CANCEL_REPORT)", + data)); + } + } +} + +static void +task_ssm_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + self->num_frames = 0; + self->task_ssm = NULL; + + if (error) + fpi_device_action_error (device, g_steal_pointer (&error)); +} + +static const char * +get_g_usb_device_direction_des (GUsbDeviceDirection dir) +{ + switch (dir) + { + case G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST: + return "G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST"; + + case G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE: + return "G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE"; + + default: + return "unknown"; + } +} + +static int +usb_claim_interface_probe (FpDevice *device, int claim, GError **error) +{ + g_autoptr(GPtrArray) interfaces = NULL; + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + int ret = -1; + int i; + + interfaces = g_usb_device_get_interfaces (fpi_device_get_usb_device (device), error); + + for (i = 0; i < interfaces->len; i++) + { + GUsbInterface *cur_iface = g_ptr_array_index (interfaces, i); + g_autoptr(GPtrArray) endpoints = g_usb_interface_get_endpoints (cur_iface); + + fp_dbg ("class:%x, subclass:%x, protocol:%x", + g_usb_interface_get_class (cur_iface), + g_usb_interface_get_subclass (cur_iface), + g_usb_interface_get_protocol (cur_iface)); + + if (claim == 1) + { + int j; + + for (j = 0; j < endpoints->len; j++) + { + GUsbEndpoint *endpoint = g_ptr_array_index (endpoints, j); + GBytes *bytes = g_usb_endpoint_get_extra (endpoint); + + fp_dbg ("bytes size:%ld", g_bytes_get_size (bytes)); + + fp_dbg ("kind:%x, max packet size:%d, poll interval:%d, refresh:%x, " + "sync address:%x, address:%x, number:%d, direction:%s", + g_usb_endpoint_get_kind (endpoint), + g_usb_endpoint_get_maximum_packet_size (endpoint), + g_usb_endpoint_get_polling_interval (endpoint), + g_usb_endpoint_get_refresh (endpoint), + g_usb_endpoint_get_synch_address (endpoint), + g_usb_endpoint_get_address (endpoint), + g_usb_endpoint_get_number (endpoint), + get_g_usb_device_direction_des (g_usb_endpoint_get_direction (endpoint))); + + if (g_usb_endpoint_get_direction (endpoint) == G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST) + self->bulk_in_ep = g_usb_endpoint_get_address (endpoint); + else + self->bulk_out_ep = g_usb_endpoint_get_address (endpoint); + } + + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), + g_usb_interface_get_number (cur_iface), + 0, error)) + return ret; + } + else if (!g_usb_device_release_interface (fpi_device_get_usb_device (device), + g_usb_interface_get_number (cur_iface), + 0, error)) + { + return ret; + } + + + } + + ret = 0; + + return ret; +} + +static void +task_ssm_init_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + if (error) + usb_claim_interface_probe (device, 0, &error); + + fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error)); +} + +struct EnrollTimes +{ + uint8_t enroll_times; +}; + +static void +focaltech_moc_get_enroll_times (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct EnrollTimes *enroll_times = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + enroll_times = (struct EnrollTimes *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + fp_dbg ("focaltechmoc enroll_times: %d", enroll_times->enroll_times + 1); + fpi_device_set_nr_enroll_stages (FP_DEVICE (self), enroll_times->enroll_times + 1); + fpi_ssm_next_state (self->task_ssm); + } +} + +static void +focaltech_moc_release_finger (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + fpi_ssm_next_state (self->task_ssm); + } +} + +enum dev_init_states { + DEV_INIT_GET_ENROLL_TIMES, + DEV_INIT_RELEASE_FINGER, + DEV_INIT_STATES, +}; + +static void +dev_init_handler (FpiSsm *ssm, FpDevice *device) +{ + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case DEV_INIT_GET_ENROLL_TIMES: + cmd_len = 0; + resp_len = sizeof (struct EnrollTimes); + cmd_buf = focaltech_moc_compose_cmd (0xa5, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enroll_times); + break; + + case DEV_INIT_RELEASE_FINGER: + { + uint8_t d1 = 0x78; + cmd_len = sizeof (d1); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_release_finger); + break; + } + } +} + +static void +focaltech_moc_open (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + GError *error = NULL; + + if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) + { + fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error)); + return; + } + + if (usb_claim_interface_probe (device, 1, &error) != 0) + { + fpi_device_open_complete (FP_DEVICE (self), g_steal_pointer (&error)); + return; + } + + self->task_ssm = fpi_ssm_new (FP_DEVICE (self), dev_init_handler, DEV_INIT_STATES); + fpi_ssm_start (self->task_ssm, task_ssm_init_done); +} + +static void +task_ssm_exit_done (FpiSsm *ssm, FpDevice *device, GError *error) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + if (!error) + { + GError *local_error = NULL; + + if (usb_claim_interface_probe (device, 0, &local_error) < 0) + g_propagate_error (&error, g_steal_pointer (&local_error)); + } + + fpi_device_close_complete (FP_DEVICE (self), error); + self->task_ssm = NULL; +} + +enum dev_exit_states { + DEV_EXIT_START, + DEV_EXIT_STATES, +}; + +static void +dev_exit_handler (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + switch (fpi_ssm_get_cur_state (ssm)) + { + case DEV_EXIT_START: + fpi_ssm_next_state (self->task_ssm); + break; + + default: + g_assert_not_reached (); + } +} + +static void +focaltech_moc_close (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + fp_info ("Focaltechmoc dev_exit"); + self->task_ssm = fpi_ssm_new (FP_DEVICE (self), dev_exit_handler, DEV_EXIT_STATES); + fpi_ssm_start (self->task_ssm, task_ssm_exit_done); +} + +enum identify_states { + MOC_IDENTIFY_RELEASE_FINGER, + MOC_IDENTIFY_WAIT_FINGER, + MOC_IDENTIFY_WAIT_FINGER_DELAY, + MOC_IDENTIFY_CAPTURE, + MOC_IDENTIFY_MATCH, + MOC_IDENTIFY_NUM_STATES, +}; + +static void +focaltech_moc_identify_wait_finger_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + uint8_t *finger_status = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + finger_status = (uint8_t *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + + if (*finger_status == 0x01) + fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_CAPTURE); + else + fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_WAIT_FINGER_DELAY); + } +} + +static void +focaltech_moc_identify_wait_finger_delay (FpDevice *device, + void *user_data) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + fpi_ssm_jump_to_state (self->task_ssm, MOC_IDENTIFY_WAIT_FINGER); +} + +enum FprintError { + ERROR_NONE, + ERROR_QUALITY, + ERROR_SHORT, + ERROR_LEFT, + ERROR_RIGHT, + ERROR_NONFINGER, + ERROR_NOMOVE, + ERROR_OTHER, +}; + +struct CaptureResult +{ + uint8_t error; + uint8_t remain; +}; + +static void +focaltech_moc_identify_capture_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct CaptureResult *capture_result = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + capture_result = (struct CaptureResult *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + if (capture_result->error == ERROR_NONE) + { + fpi_ssm_next_state (self->task_ssm); + } + else + { + if (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_VERIFY) + { + fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error); + fpi_device_verify_complete (FP_DEVICE (self), NULL); + } + else + { + fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error); + fpi_device_identify_complete (FP_DEVICE (self), NULL); + } + + fpi_ssm_mark_failed (self->task_ssm, fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + } + } +} + +static void +identify_status_report (FpiDeviceFocaltechMoc *self, FpPrint *print, GError *error) +{ + FpDevice *device = FP_DEVICE (self); + + if (print == NULL) + { + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) + { + fpi_device_identify_report (device, NULL, NULL, NULL); + fpi_device_identify_complete (device, NULL); + } + else + { + fpi_device_verify_report (device, FPI_MATCH_FAIL, NULL, NULL); + fpi_device_verify_complete (device, NULL); + } + } + else + { + if (fpi_device_get_current_action (device) == FPI_DEVICE_ACTION_IDENTIFY) + { + GPtrArray *prints; + gboolean found = FALSE; + guint index; + + fpi_device_get_identify_data (device, &prints); + found = g_ptr_array_find_with_equal_func (prints, + print, + (GEqualFunc) fp_print_equal, + &index); + + if (found) + fpi_device_identify_report (device, g_ptr_array_index (prints, index), print, NULL); + else + fpi_device_identify_report (device, NULL, print, NULL); + + fpi_device_identify_complete (device, NULL); + } + else + { + FpPrint *verify_print = NULL; + fpi_device_get_verify_data (device, &verify_print); + + if (fp_print_equal (verify_print, print)) + fpi_device_verify_report (device, FPI_MATCH_SUCCESS, print, NULL); + else + fpi_device_verify_report (device, FPI_MATCH_FAIL, print, NULL); + + fpi_device_verify_complete (device, NULL); + } + } +} + +static void +focaltech_moc_identify_match_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct UserId *user_id = NULL; + FpPrint *print = NULL; + + fp_cmd = (FpCmd *) buffer_in; + user_id = (struct UserId *) (fp_cmd + 1); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + if (fp_cmd->code == 0x04) + { + print = fp_print_new (FP_DEVICE (self)); + fprint_set_uid (print, user_id->uid, sizeof (user_id->uid)); + } + + identify_status_report (self, print, error); + + fpi_ssm_next_state (self->task_ssm); +} + +static void +focaltech_identify_run_state (FpiSsm *ssm, FpDevice *device) +{ + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_IDENTIFY_RELEASE_FINGER: + { + uint8_t d1 = 0x78; + cmd_len = sizeof (d1); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_release_finger); + break; + } + + case MOC_IDENTIFY_WAIT_FINGER: + { + uint8_t data = 0x02; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (uint8_t); + cmd_buf = focaltech_moc_compose_cmd (0x80, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_identify_wait_finger_cb); + break; + } + + case MOC_IDENTIFY_WAIT_FINGER_DELAY: + fpi_device_add_timeout (device, 50, + focaltech_moc_identify_wait_finger_delay, + NULL, NULL); + break; + + case MOC_IDENTIFY_CAPTURE: + cmd_len = 0; + resp_len = sizeof (struct CaptureResult); + cmd_buf = focaltech_moc_compose_cmd (0xa6, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_identify_capture_cb); + break; + + case MOC_IDENTIFY_MATCH: + cmd_len = 0; + resp_len = sizeof (struct UserId); + cmd_buf = focaltech_moc_compose_cmd (0xaa, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_identify_match_cb); + break; + } +} + +static void +focaltech_moc_identify (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + self->task_ssm = fpi_ssm_new (device, + focaltech_identify_run_state, + MOC_IDENTIFY_NUM_STATES); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +enum moc_enroll_states { + MOC_ENROLL_GET_ENROLLED_INFO, + MOC_ENROLL_GET_ENROLLED_LIST, + MOC_ENROLL_RELEASE_FINGER, + MOC_ENROLL_START_ENROLL, + MOC_ENROLL_WAIT_FINGER, + MOC_ENROLL_WAIT_FINGER_DELAY, + MOC_ENROLL_ENROLL_CAPTURE, + MOC_ENROLL_SET_ENROLLED_INFO, + MOC_ENROLL_COMMIT_RESULT, + MOC_ENROLL_NUM_STATES, +}; + +struct EnrolledInfoItem +{ + uint8_t uid[FOCALTECH_MOC_UID_PREFIX_LENGTH]; + uint8_t user_id[FOCALTECH_MOC_USER_ID_LENGTH]; +}; + +struct UserDes +{ + uint8_t finger; + char username[FOCALTECH_MOC_USER_ID_LENGTH]; +}; + +struct EnrolledInfo +{ + uint8_t actived[FOCALTECH_MOC_MAX_FINGERS]; + struct EnrolledInfoItem items[FOCALTECH_MOC_MAX_FINGERS]; + struct UserId user_id[FOCALTECH_MOC_MAX_FINGERS]; + struct UserDes user_des[FOCALTECH_MOC_MAX_FINGERS]; +}; + +typedef struct +{ + GPtrArray *list_result; + struct EnrolledInfo *enrolled_info; +} FpActionData; + +struct EnrolledInfoSetData +{ + uint8_t data; + struct EnrolledInfoItem items[FOCALTECH_MOC_MAX_FINGERS]; +}; + +static void +fp_action_ssm_done_data_free (FpActionData *data) +{ + g_clear_pointer (&data->list_result, g_ptr_array_unref); + g_clear_pointer (&data->enrolled_info, g_free); + g_free (data); +} + +static void +focaltech_moc_get_enrolled_info_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct EnrolledInfoItem *items = NULL; + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + items = (struct EnrolledInfoItem *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + memcpy (&data->enrolled_info->items[0], items, + FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem)); + fpi_ssm_next_state (self->task_ssm); + } +} + +struct UidList +{ + uint8_t actived[FOCALTECH_MOC_MAX_FINGERS]; + struct UserId uid[FOCALTECH_MOC_MAX_FINGERS]; +}; + +static int +focaltech_moc_get_enrolled_info_item (FpiDeviceFocaltechMoc *self, + uint8_t *uid, + struct EnrolledInfoItem **pitem, int *index) +{ + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + int ret = -1; + int i; + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + struct EnrolledInfoItem *item = &data->enrolled_info->items[i]; + + if (memcmp (item->uid, uid, FOCALTECH_MOC_UID_PREFIX_LENGTH) == 0) + { + data->enrolled_info->actived[i] = 1; + *pitem = item; + *index = i; + ret = 0; + break; + } + } + + return ret; +} + +static void +focaltech_moc_get_enrolled_list_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct UidList *uid_list = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + uid_list = (struct UidList *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + int i; + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + if (uid_list->actived[i] != 0) + { + struct UserId *user_id = &uid_list->uid[i]; + FpPrint *print = fp_print_new (FP_DEVICE (self)); + struct EnrolledInfoItem *item = NULL; + int index; + + fp_info ("focaltechmoc add slot: %d", i); + + fprint_set_uid (print, user_id->uid, sizeof (user_id->uid)); + + if (focaltech_moc_get_enrolled_info_item (self, user_id->uid, &item, &index) == 0) + { + g_autofree gchar *userid_safe = NULL; + const gchar *username; + userid_safe = g_strndup ((const char *) &item->user_id, FOCALTECH_MOC_USER_ID_LENGTH); + fp_dbg ("%s", userid_safe); + fpi_print_fill_from_user_id (print, userid_safe); + memcpy (data->enrolled_info->user_id[index].uid, user_id->uid, 32); + data->enrolled_info->user_des[index].finger = fp_print_get_finger (print); + username = fp_print_get_username (print); + + if (username != NULL) + strncpy (data->enrolled_info->user_des[index].username, username, 64); + } + + g_ptr_array_add (data->list_result, g_object_ref_sink (print)); + } + } + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + struct EnrolledInfoItem *item = &data->enrolled_info->items[i]; + + if (data->enrolled_info->actived[i] == 0) + memset (item, 0, sizeof (struct EnrolledInfoItem)); + } + + fpi_ssm_next_state (self->task_ssm); + } +} + +static void +focaltech_moc_enroll_wait_finger_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + uint8_t *finger_status = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + finger_status = (uint8_t *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER_DELAY); + } + else + { + + if (*finger_status == 0x01) + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_ENROLL_CAPTURE); + else + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER_DELAY); + } +} + +static void +focaltech_moc_enroll_wait_finger_delay (FpDevice *device, + void *user_data + ) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER); +} + +static void +focaltech_moc_start_enroll_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct UserId *user_id = NULL; + FpPrint *print = NULL; + struct EnrolledInfoItem *item = NULL; + int index; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + user_id = (struct UserId *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + if (fp_cmd->code == 0x05) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_DATA_FULL, + "device data full!!")); + } + else + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + return; + } + + if (focaltech_moc_get_enrolled_info_item (self, user_id->uid, &item, &index) == 0) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "uid error!!")); + } + else + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + g_autofree gchar *userid_safe = NULL; + gsize userid_len; + uint8_t found = 0; + int i; + struct EnrolledInfoItem *free_item = NULL; + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + item = &data->enrolled_info->items[i]; + + if (data->enrolled_info->actived[i] == 0) + { + found = 1; + free_item = item; + break; + } + } + + if (found == 0) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "no uid slot!!")); + } + else + { + fpi_device_get_enroll_data (FP_DEVICE (self), &print); + fprint_set_uid (print, user_id->uid, sizeof (user_id->uid)); + userid_safe = fpi_print_generate_user_id (print); + userid_len = strlen (userid_safe); + userid_len = MIN (FOCALTECH_MOC_USER_ID_LENGTH, userid_len); + fp_info ("focaltechmoc user id: %s", userid_safe); + memcpy (free_item->uid, user_id->uid, FOCALTECH_MOC_UID_PREFIX_LENGTH); + memcpy (free_item->user_id, userid_safe, userid_len); + fpi_ssm_next_state (self->task_ssm); + } + } +} + +static void +focaltech_moc_enroll_capture_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + struct CaptureResult *capture_result = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + capture_result = (struct CaptureResult *) (fp_cmd + 1); + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + if (capture_result->error == ERROR_NONE) + { + self->num_frames += 1; + enroll_status_report (self, ENROLL_RSP_ENROLL_REPORT, self->num_frames, NULL); + fp_info ("focaltechmoc remain: %d", capture_result->remain); + } + else + { + enroll_status_report (self, ENROLL_RSP_RETRY, self->num_frames, NULL); + } + + if (self->num_frames == fp_device_get_nr_enroll_stages (FP_DEVICE (self))) + fpi_ssm_next_state (self->task_ssm); + else + fpi_ssm_jump_to_state (self->task_ssm, MOC_ENROLL_WAIT_FINGER); + } +} + +static void +focaltech_moc_set_enrolled_info_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + return; + } + + fpi_ssm_next_state (self->task_ssm); +} + +static void +focaltech_moc_commit_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + fp_info ("focaltech_moc_commit_cb success"); + enroll_status_report (self, ENROLL_RSP_ENROLL_OK, self->num_frames, NULL); + fpi_ssm_next_state (self->task_ssm); + } +} + +static void +focaltech_enroll_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_ENROLL_GET_ENROLLED_INFO: + { + uint8_t data = 0x00; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_info_cb); + break; + } + + case MOC_ENROLL_GET_ENROLLED_LIST: + { + cmd_len = 0; + resp_len = sizeof (struct UidList); + cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_list_cb); + break; + } + + case MOC_ENROLL_RELEASE_FINGER: + { + uint8_t d1 = 0x78; + cmd_len = sizeof (d1); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0x82, &d1, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_release_finger); + break; + } + + case MOC_ENROLL_START_ENROLL: + cmd_len = 0; + resp_len = sizeof (struct UserId); + cmd_buf = focaltech_moc_compose_cmd (0xa9, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_start_enroll_cb); + break; + + + case MOC_ENROLL_WAIT_FINGER: + { + uint8_t data = 0x02; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (uint8_t); + cmd_buf = focaltech_moc_compose_cmd (0x80, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_enroll_wait_finger_cb); + break; + } + + case MOC_ENROLL_WAIT_FINGER_DELAY: + fpi_device_add_timeout (device, 50, + focaltech_moc_enroll_wait_finger_delay, + NULL, NULL); + break; + + case MOC_ENROLL_ENROLL_CAPTURE: + cmd_len = 0; + resp_len = sizeof (struct CaptureResult); + cmd_buf = focaltech_moc_compose_cmd (0xa6, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_enroll_capture_cb); + break; + + case MOC_ENROLL_SET_ENROLLED_INFO: + { + g_autofree struct EnrolledInfoSetData *set_data = NULL; + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + + cmd_len = sizeof (struct EnrolledInfoSetData); + resp_len = 0; + set_data = (struct EnrolledInfoSetData *) g_malloc0 (cmd_len); + set_data->data = 0x01; + memcpy (&set_data->items[0], &data->enrolled_info->items[0], + FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem)); + cmd_buf = focaltech_moc_compose_cmd (0xaf, (const uint8_t *) set_data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_set_enrolled_info_cb); + break; + } + + case MOC_ENROLL_COMMIT_RESULT: + { + FpPrint *print = NULL; + g_autoptr(GVariant) data = NULL; + g_autoptr(GVariant) user_id_var = NULL; + const guint8 *user_id; + gsize user_id_len = 0; + + fpi_device_get_enroll_data (FP_DEVICE (self), &print); + g_object_get (print, "fpi-data", &data, NULL); + + if (!g_variant_check_format_string (data, "(@ay)", FALSE)) + { + fpi_ssm_mark_failed (ssm, fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + g_variant_get (data, + "(@ay)", + &user_id_var); + user_id = g_variant_get_fixed_array (user_id_var, &user_id_len, 1); + + cmd_len = user_id_len; + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0xa3, user_id, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_commit_cb); + break; + } + } +} + +static void +focaltech_moc_enroll (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = g_new0 (FpActionData, 1); + + data->enrolled_info = g_new0 (struct EnrolledInfo, 1); + data->list_result = g_ptr_array_new_with_free_func (g_object_unref); + + self->task_ssm = fpi_ssm_new (FP_DEVICE (self), + focaltech_enroll_run_state, + MOC_ENROLL_NUM_STATES); + fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +static void +focaltech_moc_delete_cb (FpiDeviceFocaltechMoc *self, + uint8_t *buffer_in, + gsize length_in, + GError *error) +{ + FpCmd *fp_cmd = NULL; + + if (error) + { + fpi_ssm_mark_failed (self->task_ssm, error); + return; + } + + fp_cmd = (FpCmd *) buffer_in; + + if (fp_cmd->code != 0x04) + { + fpi_ssm_mark_failed (self->task_ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Can't get response!!")); + } + else + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + int ssm_state; + + if (self->delete_slot != -1) + { + fp_dbg ("delete slot %d", self->delete_slot); + data->enrolled_info->actived[self->delete_slot] = 0; + memset (&data->enrolled_info->items[self->delete_slot], 0, sizeof (struct EnrolledInfoItem)); + memset (&data->enrolled_info->user_id[self->delete_slot], 0, sizeof (struct UserId)); + memset (&data->enrolled_info->user_des[self->delete_slot], 0, sizeof (struct UserDes)); + } + + ssm_state = fpi_ssm_get_cur_state (self->task_ssm); + fpi_ssm_jump_to_state (self->task_ssm, ssm_state); + } +} + +enum delete_states { + MOC_DELETE_GET_ENROLLED_INFO, + MOC_DELETE_GET_ENROLLED_LIST, + MOC_DELETE_SET_ENROLLED_INFO, + MOC_DELETE_BY_UID, + MOC_DELETE_BY_USER_INFO, + MOC_DELETE_NUM_STATES, +}; + +static void +focaltech_delete_run_state (FpiSsm *ssm, FpDevice *device) +{ + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_DELETE_GET_ENROLLED_INFO: + { + uint8_t data = 0x00; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_info_cb); + break; + } + + case MOC_DELETE_GET_ENROLLED_LIST: + { + cmd_len = 0; + resp_len = sizeof (struct UidList) + sizeof (struct UserId) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_list_cb); + break; + } + + case MOC_DELETE_SET_ENROLLED_INFO: + { + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + g_autofree struct EnrolledInfoSetData *set_data = NULL; + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + + cmd_len = sizeof (struct EnrolledInfoSetData); + resp_len = 0; + set_data = (struct EnrolledInfoSetData *) g_malloc0 (cmd_len); + set_data->data = 0x01; + memcpy (&set_data->items[0], &data->enrolled_info->items[0], FOCALTECH_MOC_MAX_FINGERS * sizeof (struct EnrolledInfoItem)); + cmd_buf = focaltech_moc_compose_cmd (0xaf, (const uint8_t *) set_data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_set_enrolled_info_cb); + break; + } + + case MOC_DELETE_BY_UID: + { + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpPrint *print = NULL; + g_autoptr(GVariant) data = NULL; + g_autoptr(GVariant) user_id_var = NULL; + const guint8 *user_id; + gsize user_id_len = 0; + struct EnrolledInfoItem *item = NULL; + int index; + + self->delete_slot = -1; + fpi_device_get_delete_data (device, &print); + g_object_get (print, "fpi-data", &data, NULL); + + if (!g_variant_check_format_string (data, "(@ay)", FALSE)) + { + fpi_device_delete_complete (device, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + g_variant_get (data, "(@ay)", &user_id_var); + user_id = g_variant_get_fixed_array (user_id_var, &user_id_len, 1); + + if (focaltech_moc_get_enrolled_info_item (self, (uint8_t *) user_id, &item, &index) == 0) + self->delete_slot = index; + + if (self->delete_slot != -1) + { + cmd_len = sizeof (struct UserId); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0xa8, user_id, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_delete_cb); + } + else + { + fpi_ssm_next_state (self->task_ssm); + } + + break; + } + + case MOC_DELETE_BY_USER_INFO: + { + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + FpPrint *print = NULL; + const guint8 *user_id; + const gchar *username; + uint8_t finger; + int i; + + self->delete_slot = -1; + fpi_device_get_delete_data (device, &print); + username = fp_print_get_username (print); + finger = fp_print_get_finger (print); + + for (i = 0; i < FOCALTECH_MOC_MAX_FINGERS; i++) + { + struct UserDes *user_des = &data->enrolled_info->user_des[i]; + + if (username == NULL) + continue; + + if (strncmp (user_des->username, username, FOCALTECH_MOC_USER_ID_LENGTH) != 0) + continue; + + if (finger != user_des->finger) + continue; + + self->delete_slot = i; + } + + if (self->delete_slot != -1) + { + user_id = (const guint8 *) &data->enrolled_info->user_id[self->delete_slot].uid[0]; + cmd_len = sizeof (struct UserId); + resp_len = 0; + cmd_buf = focaltech_moc_compose_cmd (0xa8, user_id, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_delete_cb); + } + else + { + fpi_device_delete_complete (FP_DEVICE (self), NULL); + fpi_ssm_next_state (self->task_ssm); + } + + break; + } + } +} + +static void +focaltech_moc_delete_print (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = g_new0 (FpActionData, 1); + + data->enrolled_info = g_new0 (struct EnrolledInfo, 1); + data->list_result = g_ptr_array_new_with_free_func (g_object_unref); + + self->task_ssm = fpi_ssm_new (device, + focaltech_delete_run_state, + MOC_DELETE_NUM_STATES); + fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +enum moc_list_states { + MOC_LIST_GET_ENROLLED_INFO, + MOC_LIST_GET_ENROLLED_LIST, + MOC_LIST_REPORT, + MOC_LIST_NUM_STATES, +}; + +static void +focaltech_list_run_state (FpiSsm *ssm, FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + guint8 *cmd_buf = NULL; + uint16_t cmd_len = 0; + uint16_t resp_len = 0; + + switch (fpi_ssm_get_cur_state (ssm)) + { + case MOC_LIST_GET_ENROLLED_INFO: + { + uint8_t data = 0x00; + cmd_len = sizeof (uint8_t); + resp_len = sizeof (struct EnrolledInfoItem) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xaf, &data, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_info_cb); + break; + } + + case MOC_LIST_GET_ENROLLED_LIST: + { + cmd_len = 0; + resp_len = sizeof (struct UidList) + sizeof (struct UserId) * FOCALTECH_MOC_MAX_FINGERS; + cmd_buf = focaltech_moc_compose_cmd (0xab, NULL, cmd_len); + focaltech_moc_get_cmd (device, cmd_buf, + sizeof (FpCmd) + cmd_len + sizeof (uint8_t), + sizeof (FpCmd) + resp_len + sizeof (uint8_t), + 1, + focaltech_moc_get_enrolled_list_cb); + break; + } + + case MOC_LIST_REPORT: + { + FpActionData *data = fpi_ssm_get_data (self->task_ssm); + fpi_device_list_complete (FP_DEVICE (self), g_steal_pointer (&data->list_result), NULL); + fpi_ssm_next_state (self->task_ssm); + break; + } + } +} + +static void +focaltech_moc_list (FpDevice *device) +{ + FpiDeviceFocaltechMoc *self = FPI_DEVICE_FOCALTECH_MOC (device); + FpActionData *data = g_new0 (FpActionData, 1); + + data->enrolled_info = g_new0 (struct EnrolledInfo, 1); + data->list_result = g_ptr_array_new_with_free_func (g_object_unref); + self->task_ssm = fpi_ssm_new (device, + focaltech_list_run_state, + MOC_LIST_NUM_STATES); + fpi_ssm_set_data (self->task_ssm, data, (GDestroyNotify) fp_action_ssm_done_data_free); + fpi_ssm_start (self->task_ssm, task_ssm_done); +} + +static void +fpi_device_focaltech_moc_init (FpiDeviceFocaltechMoc *self) +{ + G_DEBUG_HERE (); +} + +static void +fpi_device_focaltech_moc_class_init (FpiDeviceFocaltechMocClass *klass) +{ + FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); + + dev_class->id = FP_COMPONENT; + dev_class->full_name = FOCALTECH_MOC_DRIVER_FULLNAME; + + dev_class->type = FP_DEVICE_TYPE_USB; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + dev_class->id_table = id_table; + dev_class->nr_enroll_stages = FOCALTECH_MOC_MAX_FINGERS; + dev_class->temp_hot_seconds = -1; + + dev_class->open = focaltech_moc_open; + dev_class->close = focaltech_moc_close; + dev_class->verify = focaltech_moc_identify; + dev_class->enroll = focaltech_moc_enroll; + dev_class->identify = focaltech_moc_identify; + dev_class->delete = focaltech_moc_delete_print; + dev_class->list = focaltech_moc_list; + + fpi_device_class_auto_initialize_features (dev_class); +} diff --git a/libfprint/drivers/focaltech_moc/focaltech_moc.h b/libfprint/drivers/focaltech_moc/focaltech_moc.h new file mode 100644 index 00000000..9fcbec5d --- /dev/null +++ b/libfprint/drivers/focaltech_moc/focaltech_moc.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 Focaltech Microelectronics + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "fpi-device.h" +#include "fpi-ssm.h" +#include + +#include +#include + +G_DECLARE_FINAL_TYPE (FpiDeviceFocaltechMoc, fpi_device_focaltech_moc, FPI, DEVICE_FOCALTECH_MOC, FpDevice) + +#define FOCALTECH_MOC_DRIVER_FULLNAME "Focaltech MOC Sensors" + +#define FOCALTECH_MOC_CMD_TIMEOUT 1000 +#define FOCALTECH_MOC_MAX_FINGERS 10 +#define FOCALTECH_MOC_UID_PREFIX_LENGTH 8 +#define FOCALTECH_MOC_USER_ID_LENGTH 64 + +typedef void (*FocaltechCmdMsgCallback) (FpiDeviceFocaltechMoc *self, + GError *error); + +struct _FpiDeviceFocaltechMoc +{ + FpDevice parent; + FpiSsm *task_ssm; + FpiSsm *cmd_ssm; + FpiUsbTransfer *cmd_transfer; + gboolean cmd_cancelable; + gsize cmd_len_in; + int num_frames; + int delete_slot; + guint8 bulk_in_ep; + guint8 bulk_out_ep; +}; diff --git a/libfprint/meson.build b/libfprint/meson.build index c2ebd8c1..da8285e5 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -143,6 +143,8 @@ driver_sources = { [ 'drivers/fpcmoc/fpc.c' ], 'realtek' : [ 'drivers/realtek/realtek.c' ], + 'focaltech_moc' : + [ 'drivers/focaltech_moc/focaltech_moc.c' ], } helper_sources = { diff --git a/meson.build b/meson.build index 9fc10315..aeef6911 100644 --- a/meson.build +++ b/meson.build @@ -132,6 +132,7 @@ default_drivers = [ 'nb1010', 'fpcmoc', 'realtek', + 'focaltech_moc', # SPI 'elanspi', diff --git a/tests/focaltech_moc/custom.pcapng b/tests/focaltech_moc/custom.pcapng new file mode 100644 index 00000000..9bcd05c9 Binary files /dev/null and b/tests/focaltech_moc/custom.pcapng differ diff --git a/tests/focaltech_moc/custom.py b/tests/focaltech_moc/custom.py new file mode 100755 index 00000000..6a876c60 --- /dev/null +++ b/tests/focaltech_moc/custom.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 + +import traceback +import sys +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 + +assert d.get_driver() == "focaltech_moc" +assert not d.has_feature(FPrint.DeviceFeature.CAPTURE) +assert d.has_feature(FPrint.DeviceFeature.IDENTIFY) +assert d.has_feature(FPrint.DeviceFeature.VERIFY) +assert not 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 not d.has_feature(FPrint.DeviceFeature.STORAGE_CLEAR) + +d.open_sync() + +template = FPrint.Print.new(d) + +def enroll_progress(*args): + #assert d.get_finger_status() == FPrint.FingerStatusFlags.NEEDED + 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) + +# List, enroll, list, verify, identify, delete +print("enrolling") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +p = d.enroll_sync(template, None, enroll_progress, None) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("enroll done") + +print("listing") +stored = d.list_prints_sync() +print("listing done") +assert len(stored) == 1 +assert stored[0].equal(p) +print("verifying") +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +verify_res, verify_print = d.verify_sync(p) +assert d.get_finger_status() == FPrint.FingerStatusFlags.NONE +print("verify done") +del p +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("deleting") +d.delete_print_sync(p) +print("delete done") + +d.close_sync() + +del d +del c diff --git a/tests/focaltech_moc/device b/tests/focaltech_moc/device new file mode 100644 index 00000000..093807d1 --- /dev/null +++ b/tests/focaltech_moc/device @@ -0,0 +1,385 @@ +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0/usb3/3-1/3-1.4 +N: bus/usb/003/006=1201100100000040082879D900020102030109022000010100A0320904000002DCA0B0000705020240000007058102400000 +E: DEVNAME=/dev/bus/usb/003/006 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=2808/d979/200 +E: TYPE=0/0/0 +E: BUSNUM=003 +E: DEVNUM=006 +E: MAJOR=189 +E: MINOR=261 +E: SUBSYSTEM=usb +E: ID_VENDOR=CCore +E: ID_VENDOR_ENC=CCore +E: ID_VENDOR_ID=2808 +E: ID_MODEL=FocalTech_FT9349_ESS +E: ID_MODEL_ENC=FocalTech\x20FT9349\x20ESS +E: ID_MODEL_ID=d979 +E: ID_REVISION=0200 +E: ID_SERIAL=CCore_FocalTech_FT9349_ESS_1234567890ABCDEF +E: ID_SERIAL_SHORT=1234567890ABCDEF +E: ID_BUS=usb +E: ID_USB_INTERFACES=:dca0b0: +E: ID_PATH=pci-0000:0b:00.0-usb-0:1.4 +E: ID_PATH_TAG=pci-0000_0b_00_0-usb-0_1_4 +A: authorized=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=00 +A: bDeviceProtocol=00 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=100mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=0200 +A: bmAttributes=a0 +A: busnum=3 +A: configuration= +H: descriptors=1201100100000040082879D900020102030109022000010100A0320904000002DCA0B0000705020240000007058102400000 +A: dev=189:261 +A: devnum=6 +A: devpath=1.4 +L: driver=../../../../../../../bus/usb/drivers/usb +A: idProduct=d979 +A: idVendor=2808 +A: ltm_capable=no +A: manufacturer=CCore +A: maxchild=0 +L: port=../3-1:1.0/3-1-port4 +A: power/active_duration=130884 +A: power/async=enabled +A: power/autosuspend=2 +A: power/autosuspend_delay_ms=2000 +A: power/connected_duration=2778952 +A: power/control=auto +A: power/level=auto +A: power/persist=0 +A: power/runtime_active_kids=0 +A: power/runtime_active_time=131747 +A: power/runtime_enabled=enabled +A: power/runtime_status=active +A: power/runtime_suspended_time=2647026 +A: power/runtime_usage=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=FocalTech FT9349 ESS +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: serial=1234567890ABCDEF +A: speed=12 +A: tx_lanes=1 +A: urbnum=1922 +A: version= 1.10 + +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0/usb3/3-1 +N: bus/usb/003/002=1201000209000140E305080636850001000109021900010100E0320904000001090000000705810301000C +E: DEVNAME=/dev/bus/usb/003/002 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=5e3/608/8536 +E: TYPE=9/0/1 +E: BUSNUM=003 +E: DEVNUM=002 +E: MAJOR=189 +E: MINOR=257 +E: SUBSYSTEM=usb +E: ID_VENDOR=05e3 +E: ID_VENDOR_ENC=05e3 +E: ID_VENDOR_ID=05e3 +E: ID_MODEL=USB2.0_Hub +E: ID_MODEL_ENC=USB2.0\x20Hub +E: ID_MODEL_ID=0608 +E: ID_REVISION=8536 +E: ID_SERIAL=05e3_USB2.0_Hub +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Genesys Logic, Inc. +E: ID_MODEL_FROM_DATABASE=Hub +E: ID_PATH=pci-0000:0b:00.0-usb-0:1 +E: ID_PATH_TAG=pci-0000_0b_00_0-usb-0_1 +E: ID_FOR_SEAT=usb-pci-0000_0b_00_0-usb-0_1 +E: TAGS=:seat: +A: authorized=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=09 +A: bDeviceProtocol=01 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=100mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=8536 +A: bmAttributes=e0 +A: busnum=3 +A: configuration= +H: descriptors=1201000209000140E305080636850001000109021900010100E0320904000001090000000705810301000C +A: dev=189:257 +A: devnum=2 +A: devpath=1 +L: driver=../../../../../../bus/usb/drivers/usb +A: idProduct=0608 +A: idVendor=05e3 +A: ltm_capable=no +A: maxchild=4 +L: port=../3-0:1.0/usb3-port1 +A: power/active_duration=6193132 +A: power/async=enabled +A: power/autosuspend=0 +A: power/autosuspend_delay_ms=0 +A: power/connected_duration=6193132 +A: power/control=auto +A: power/level=auto +A: power/runtime_active_kids=3 +A: power/runtime_active_time=6192633 +A: power/runtime_enabled=enabled +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=USB2.0 Hub +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: speed=480 +A: tx_lanes=1 +A: urbnum=619 +A: version= 2.00 + +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0/usb3 +N: bus/usb/003/001=12010002090001406B1D020015050302010109021900010100E0000904000001090000000705810304000C +E: DEVNAME=/dev/bus/usb/003/001 +E: DEVTYPE=usb_device +E: DRIVER=usb +E: PRODUCT=1d6b/2/515 +E: TYPE=9/0/1 +E: BUSNUM=003 +E: DEVNUM=001 +E: MAJOR=189 +E: MINOR=256 +E: SUBSYSTEM=usb +E: ID_VENDOR=Linux_5.15.0-52-generic_xhci-hcd +E: ID_VENDOR_ENC=Linux\x205.15.0-52-generic\x20xhci-hcd +E: ID_VENDOR_ID=1d6b +E: ID_MODEL=xHCI_Host_Controller +E: ID_MODEL_ENC=xHCI\x20Host\x20Controller +E: ID_MODEL_ID=0002 +E: ID_REVISION=0515 +E: ID_SERIAL=Linux_5.15.0-52-generic_xhci-hcd_xHCI_Host_Controller_0000:0b:00.0 +E: ID_SERIAL_SHORT=0000:0b:00.0 +E: ID_BUS=usb +E: ID_USB_INTERFACES=:090000: +E: ID_VENDOR_FROM_DATABASE=Linux Foundation +E: ID_MODEL_FROM_DATABASE=2.0 root hub +E: ID_PATH=pci-0000:0b:00.0 +E: ID_PATH_TAG=pci-0000_0b_00_0 +E: ID_FOR_SEAT=usb-pci-0000_0b_00_0 +E: TAGS=:seat: +A: authorized=1 +A: authorized_default=1 +A: avoid_reset_quirk=0 +A: bConfigurationValue=1 +A: bDeviceClass=09 +A: bDeviceProtocol=01 +A: bDeviceSubClass=00 +A: bMaxPacketSize0=64 +A: bMaxPower=0mA +A: bNumConfigurations=1 +A: bNumInterfaces= 1 +A: bcdDevice=0515 +A: bmAttributes=e0 +A: busnum=3 +A: configuration= +H: descriptors=12010002090001406B1D020015050302010109021900010100E0000904000001090000000705810304000C +A: dev=189:256 +A: devnum=1 +A: devpath=0 +L: driver=../../../../../bus/usb/drivers/usb +A: idProduct=0002 +A: idVendor=1d6b +A: interface_authorized_default=1 +A: ltm_capable=no +A: manufacturer=Linux 5.15.0-52-generic xhci-hcd +A: maxchild=2 +A: power/active_duration=6193348 +A: power/async=enabled +A: power/autosuspend=0 +A: power/autosuspend_delay_ms=0 +A: power/connected_duration=6193348 +A: power/control=auto +A: power/level=auto +A: power/runtime_active_kids=1 +A: power/runtime_active_time=6193145 +A: power/runtime_enabled=enabled +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=0 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: product=xHCI Host Controller +A: quirks=0x0 +A: removable=unknown +A: rx_lanes=1 +A: serial=0000:0b:00.0 +A: speed=480 +A: tx_lanes=1 +A: urbnum=36 +A: version= 2.00 + +P: /devices/pci0000:00/0000:00:1c.4/0000:0b:00.0 +E: DRIVER=xhci_hcd +E: PCI_CLASS=C0330 +E: PCI_ID=104C:8241 +E: PCI_SUBSYS_ID=1028:050F +E: PCI_SLOT_NAME=0000:0b:00.0 +E: MODALIAS=pci:v0000104Cd00008241sv00001028sd0000050Fbc0Csc03i30 +E: SUBSYSTEM=pci +E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E: ID_VENDOR_FROM_DATABASE=Texas Instruments +E: ID_MODEL_FROM_DATABASE=TUSB73x0 SuperSpeed USB 3.0 xHCI Host Controller +A: aer_dev_correctable=RxErr 0\nBadTLP 0\nBadDLLP 0\nRollover 0\nTimeout 0\nNonFatalErr 0\nCorrIntErr 0\nHeaderOF 0\nTOTAL_ERR_COR 0 +A: aer_dev_fatal=Undefined 0\nDLP 0\nSDES 0\nTLP 0\nFCP 0\nCmpltTO 0\nCmpltAbrt 0\nUnxCmplt 0\nRxOF 0\nMalfTLP 0\nECRC 0\nUnsupReq 0\nACSViol 0\nUncorrIntErr 0\nBlockedTLP 0\nAtomicOpBlocked 0\nTLPBlockedErr 0\nPoisonTLPBlocked 0\nTOTAL_ERR_FATAL 0 +A: aer_dev_nonfatal=Undefined 0\nDLP 0\nSDES 0\nTLP 0\nFCP 0\nCmpltTO 0\nCmpltAbrt 0\nUnxCmplt 0\nRxOF 0\nMalfTLP 0\nECRC 0\nUnsupReq 0\nACSViol 0\nUncorrIntErr 0\nBlockedTLP 0\nAtomicOpBlocked 0\nTLPBlockedErr 0\nPoisonTLPBlocked 0\nTOTAL_ERR_NONFATAL 0 +A: ari_enabled=0 +A: broken_parity_status=0 +A: class=0x0c0330 +H: config=4C104182060410000230030C100000000400D0F7000000000400D1F70000000000000000000000000000000028100F050000000040000000000000000B010000014883FE080000000570860000000000000000000000000000000000000000003020000000000000000000000000000010C00200C38F900500201900123C07004200121000000000000000000000000000000000100000000000000000000000020000000000000000000000000000000000000000000F0000000000000000001100078002000000021000000000000028100F05AB0D00001B0000003F000000000040CB00000000000000000000000000000000000000000000000000000000010002150000000000000000302046000020000000200000A000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030001000000200000280008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003B00B100FFFFFFFF04000007000F0F1B2001010000000000AA430000800200000000000000000000117E7C031000000830C0000001800000FFFF0F00000000000F00000000000000855023000B50230000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F010000000000000000000000000000000000001B0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +A: consistent_dma_mask_bits=64 +A: current_link_speed=5.0 GT/s PCIe +A: current_link_width=1 +A: d3cold_allowed=1 +A: device=0x8241 +A: dma_mask_bits=64 +L: driver=../../../../bus/pci/drivers/xhci_hcd +A: driver_override=(null) +A: enable=1 +A: irq=16 +A: link/clkpm=0 +A: link/l0s_aspm=0 +A: link/l1_aspm=1 +A: local_cpulist=0-3 +A: local_cpus=f +A: max_link_speed=5.0 GT/s PCIe +A: max_link_width=1 +A: modalias=pci:v0000104Cd00008241sv00001028sd0000050Fbc0Csc03i30 +A: msi_bus=1 +A: msi_irqs/25=msix +A: msi_irqs/26=msix +A: msi_irqs/27=msix +A: msi_irqs/28=msix +A: msi_irqs/29=msix +A: numa_node=-1 +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 11 12 2112 12\nxHCI ring segments 34 34 4096 34\nbuffer-2048 16 32 2048 16\nbuffer-512 0 0 512 0\nbuffer-128 26 32 128 1\nbuffer-32 0 0 32 0 +A: power/async=enabled +A: power/control=on +A: power/runtime_active_kids=1 +A: power/runtime_active_time=6193932 +A: power/runtime_enabled=forbidden +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=1 +A: power/wakeup=enabled +A: power/wakeup_abort_count=0 +A: power/wakeup_active=0 +A: power/wakeup_active_count=0 +A: power/wakeup_count=0 +A: power/wakeup_expire_count=0 +A: power/wakeup_last_time_ms=0 +A: power/wakeup_max_time_ms=0 +A: power/wakeup_total_time_ms=0 +A: power_state=D0 +A: reset_method=bus +A: resource=0x00000000f7d00000 0x00000000f7d0ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000f7d10000 0x00000000f7d11fff 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 +A: revision=0x02 +A: subsystem_device=0x050f +A: subsystem_vendor=0x1028 +A: vendor=0x104c + +P: /devices/pci0000:00/0000:00:1c.4 +E: DRIVER=pcieport +E: PCI_CLASS=60400 +E: PCI_ID=8086:1C18 +E: PCI_SUBSYS_ID=1028:050F +E: PCI_SLOT_NAME=0000:00:1c.4 +E: MODALIAS=pci:v00008086d00001C18sv00001028sd0000050Fbc06sc04i00 +E: SUBSYSTEM=pci +E: ID_PCI_CLASS_FROM_DATABASE=Bridge +E: ID_PCI_SUBCLASS_FROM_DATABASE=PCI bridge +E: ID_PCI_INTERFACE_FROM_DATABASE=Normal decode +E: ID_VENDOR_FROM_DATABASE=Intel Corporation +E: ID_MODEL_FROM_DATABASE=6 Series/C200 Series Chipset Family PCI Express Root Port 5 +A: ari_enabled=0 +A: broken_parity_status=0 +A: class=0x060400 +H: config=8680181C07001000B5000406100081000000000000000000000B0C00F0000020D0F7D0F7F1FF010000000000000000000000000040000000000000000B011200108042010080000000001000123C1205420012F000B2240000004001000000000000000016000000000000000000000002000000000000000000000000000000059000000000000000000000000000000DA0000028100F050000000000000000010002C8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001020B00000080118100000000003F00000000000001000000000000000000000000000000870F050800000000000000000000000000000000110006000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B363A7400001414311742005B6009002020000A521498095104690616000028BCB5BC4A00000000744C8500DC08DC0061091100D30F07005000E2005B00170001009400370494000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +A: consistent_dma_mask_bits=32 +A: current_link_speed=5.0 GT/s PCIe +A: current_link_width=1 +A: d3cold_allowed=1 +A: device=0x1c18 +A: dma_mask_bits=32 +L: driver=../../../bus/pci/drivers/pcieport +A: driver_override=(null) +A: enable=2 +A: irq=16 +A: local_cpulist=0-3 +A: local_cpus=f +A: max_link_speed=5.0 GT/s PCIe +A: max_link_width=1 +A: modalias=pci:v00008086d00001C18sv00001028sd0000050Fbc06sc04i00 +A: msi_bus=1 +A: numa_node=-1 +A: power/async=enabled +A: power/control=on +A: power/runtime_active_kids=1 +A: power/runtime_active_time=6193944 +A: power/runtime_enabled=forbidden +A: power/runtime_status=active +A: power/runtime_suspended_time=0 +A: power/runtime_usage=2 +A: power/wakeup=disabled +A: power/wakeup_abort_count= +A: power/wakeup_active= +A: power/wakeup_active_count= +A: power/wakeup_count= +A: power/wakeup_expire_count= +A: power/wakeup_last_time_ms= +A: power/wakeup_max_time_ms= +A: power/wakeup_total_time_ms= +A: power_state=D0 +A: reset_method=pm +A: resource=0x0000000000000000 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\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x00000000f7d00000 0x00000000f7dfffff 0x0000000000000200\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000 +A: revision=0xb5 +A: secondary_bus_number=11 +A: subordinate_bus_number=12 +A: subsystem_device=0x050f +A: subsystem_vendor=0x1028 +A: vendor=0x8086 + diff --git a/tests/meson.build b/tests/meson.build index f4f15979..48c83037 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -53,6 +53,7 @@ drivers_tests = [ 'egis0570', 'fpcmoc', 'realtek', + 'focaltech_moc', ] if get_option('introspection')