Commit 5089a143 authored by Andreas Zschunke's avatar Andreas Zschunke Committed by Uwe Hermann

hantek-4032l: Add initial driver implementation.

[Note: This patch is basically a squashed version of the initial driver
commits by Andreas Zschunke <andreas.zschunke@gmx.net>, two fixes by
Andrej Valek <andy@skyrain.eu>, and various coding style / cosmetic
fixes by Uwe Hermann <uwe@hermann-uwe.de> to make the driver a lot more
consistent with the rest of the libsigrok code-base.]
parent 6a25fa42
...@@ -324,18 +324,18 @@ src_libdrivers_la_SOURCES += \ ...@@ -324,18 +324,18 @@ src_libdrivers_la_SOURCES += \
src/hardware/hameg-hmo/protocol.c \ src/hardware/hameg-hmo/protocol.c \
src/hardware/hameg-hmo/api.c src/hardware/hameg-hmo/api.c
endif endif
if HW_HANTEK_6XXX
src_libdrivers_la_SOURCES += \
src/hardware/hantek-6xxx/protocol.h \
src/hardware/hantek-6xxx/protocol.c \
src/hardware/hantek-6xxx/api.c
endif
if HW_HANTEK_4032L if HW_HANTEK_4032L
src_libdrivers_la_SOURCES += \ src_libdrivers_la_SOURCES += \
src/hardware/hantek-4032l/protocol.h \ src/hardware/hantek-4032l/protocol.h \
src/hardware/hantek-4032l/protocol.c \ src/hardware/hantek-4032l/protocol.c \
src/hardware/hantek-4032l/api.c src/hardware/hantek-4032l/api.c
endif endif
if HW_HANTEK_6XXX
src_libdrivers_la_SOURCES += \
src/hardware/hantek-6xxx/protocol.h \
src/hardware/hantek-6xxx/protocol.c \
src/hardware/hantek-6xxx/api.c
endif
if HW_HANTEK_DSO if HW_HANTEK_DSO
src_libdrivers_la_SOURCES += \ src_libdrivers_la_SOURCES += \
src/hardware/hantek-dso/protocol.h \ src/hardware/hantek-dso/protocol.h \
......
...@@ -240,7 +240,7 @@ SR_DRIVER([fx2lafw], [fx2lafw], [libusb]) ...@@ -240,7 +240,7 @@ SR_DRIVER([fx2lafw], [fx2lafw], [libusb])
SR_DRIVER([GMC MH 1x/2x], [gmc-mh-1x-2x], [libserialport]) SR_DRIVER([GMC MH 1x/2x], [gmc-mh-1x-2x], [libserialport])
SR_DRIVER([GW Instek GDS-800], [gwinstek-gds-800], [libserialport]) SR_DRIVER([GW Instek GDS-800], [gwinstek-gds-800], [libserialport])
SR_DRIVER([Hameg HMO], [hameg-hmo], [libserialport]) SR_DRIVER([Hameg HMO], [hameg-hmo], [libserialport])
SR_DRIVER([Hantek 4032L], [hantek-4032l]) SR_DRIVER([Hantek 4032L], [hantek-4032l], [libusb])
SR_DRIVER([Hantek 6xxx], [hantek-6xxx], [libusb]) SR_DRIVER([Hantek 6xxx], [hantek-6xxx], [libusb])
SR_DRIVER([Hantek DSO], [hantek-dso], [libusb]) SR_DRIVER([Hantek DSO], [hantek-dso], [libusb])
SR_DRIVER([HP 3457A], [hp-3457a]) SR_DRIVER([HP 3457A], [hp-3457a])
......
This diff is collapsed.
/* /*
* This file is part of the libsigrok project. * This file is part of the libsigrok project.
* *
* Copyright (C) 2016 Andreas Zschunke <andreasz77@gmx.net> * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
* Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
* Copyright (C) 2017 Uwe Hermann <uwe@hermann-uwe.de>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -20,22 +22,270 @@ ...@@ -20,22 +22,270 @@
#include <config.h> #include <config.h>
#include "protocol.h" #include "protocol.h"
SR_PRIV int hantek_4032l_receive_data(int fd, int revents, void *cb_data) #define H4032L_USB_TIMEOUT 500
enum h4032l_cmd {
CMD_CONFIGURE = 0x2b1a, /* Also arms the logic analyzer. */
CMD_STATUS = 0x4b3a,
CMD_GET = 0x6b5a
};
struct __attribute__((__packed__)) h4032l_status_packet {
uint32_t magic;
uint32_t values;
uint32_t status;
};
SR_PRIV int h4032l_receive_data(int fd, int revents, void *cb_data)
{ {
const struct sr_dev_inst *sdi; struct timeval tv;
struct dev_context *devc; struct drv_context *drvc;
(void)fd; (void)fd;
(void)revents;
drvc = (struct drv_context *)cb_data;
tv.tv_sec = tv.tv_usec = 0;
libusb_handle_events_timeout(drvc->sr_ctx->libusb_ctx, &tv);
if (!(sdi = cb_data)) return TRUE;
return TRUE; }
if (!(devc = sdi->priv)) void LIBUSB_CALL h4032l_usb_callback(struct libusb_transfer *transfer)
return TRUE; {
const struct sr_dev_inst *sdi = transfer->user_data;
struct dev_context *devc = sdi->priv;
struct drv_context *drvc = sdi->driver->context;
struct sr_usb_dev_inst *usb = sdi->conn;
gboolean cmd = FALSE;
uint32_t max_samples = 512 / sizeof(uint32_t);
uint32_t *buffer;
struct h4032l_status_packet *status;
struct sr_datafeed_packet packet;
struct sr_datafeed_logic logic;
uint32_t number_samples;
int ret;
if (revents == G_IO_IN) { if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
/* TODO */ sr_err("%s error: %d.", __func__, transfer->status);
return;
} }
return TRUE; buffer = (uint32_t *)transfer->buffer;
switch (devc->status) {
case H4032L_STATUS_IDLE:
sr_err("USB callback called in idle.");
break;
case H4032L_STATUS_CMD_CONFIGURE:
/* Select status request as next. */
cmd = TRUE;
devc->cmd_pkt.cmd = CMD_STATUS;
devc->status = H4032L_STATUS_CMD_STATUS;
break;
case H4032L_STATUS_CMD_STATUS:
/* Select status request as next. */
devc->status = H4032L_STATUS_RESPONSE_STATUS;
break;
case H4032L_STATUS_RESPONSE_STATUS:
/*
* Check magic and if status is complete, then select
* First Transfer as next.
*/
status = (struct h4032l_status_packet *)transfer->buffer;
if (status->magic != H4032L_STATUS_PACKET_MAGIC) {
devc->status = H4032L_STATUS_CMD_STATUS;
devc->cmd_pkt.cmd = CMD_STATUS;
cmd = TRUE;
} else if (status->status == 2) {
devc->status = H4032L_STATUS_RESPONSE_STATUS_CONTINUE;
} else {
devc->status = H4032L_STATUS_RESPONSE_STATUS_RETRY;
}
break;
case H4032L_STATUS_RESPONSE_STATUS_RETRY:
devc->status = H4032L_STATUS_CMD_STATUS;
devc->cmd_pkt.cmd = CMD_STATUS;
cmd = TRUE;
break;
case H4032L_STATUS_RESPONSE_STATUS_CONTINUE:
devc->status = H4032L_STATUS_CMD_GET;
devc->cmd_pkt.cmd = CMD_GET;
cmd = TRUE;
break;
case H4032L_STATUS_CMD_GET:
devc->status = H4032L_STATUS_FIRST_TRANSFER;
break;
case H4032L_STATUS_FIRST_TRANSFER:
if (buffer[0] != H4032L_START_PACKET_MAGIC) {
sr_err("Mismatch magic number of start poll.");
devc->status = H4032L_STATUS_IDLE;
break;
}
devc->status = H4032L_STATUS_TRANSFER;
max_samples--;
buffer++;
break;
case H4032L_STATUS_TRANSFER:
number_samples = (devc->remaining_samples < max_samples) ? devc->remaining_samples : max_samples;
devc->remaining_samples -= number_samples;
packet.type = SR_DF_LOGIC;
packet.payload = &logic;
logic.length = number_samples * sizeof(uint32_t);
logic.unitsize = sizeof(uint32_t);
logic.data = buffer;
sr_session_send(sdi, &packet);
sr_dbg("Remaining: %d %08X %08X.", devc->remaining_samples,
buffer[0], buffer[1]);
if (devc->remaining_samples == 0) {
std_session_send_df_end(sdi);
usb_source_remove(sdi->session, drvc->sr_ctx);
devc->status = H4032L_STATUS_IDLE;
if (buffer[number_samples] != H4032L_END_PACKET_MAGIC)
sr_err("Mismatch magic number of end poll.");
}
break;
}
if (devc->status != H4032L_STATUS_IDLE) {
if (cmd) {
/* Setup new USB cmd packet, reuse transfer object. */
sr_dbg("New command: %d.", devc->status);
libusb_fill_bulk_transfer(transfer, usb->devhdl,
2 | LIBUSB_ENDPOINT_OUT,
(unsigned char *)&devc->cmd_pkt,
sizeof(struct h4032l_cmd_pkt),
h4032l_usb_callback, (void *)sdi,
H4032L_USB_TIMEOUT);
} else {
/* Setup new USB poll packet, reuse transfer object. */
sr_dbg("Poll: %d.", devc->status);
libusb_fill_bulk_transfer(transfer, usb->devhdl,
6 | LIBUSB_ENDPOINT_IN,
devc->buffer, ARRAY_SIZE(devc->buffer),
h4032l_usb_callback,
(void *)sdi, H4032L_USB_TIMEOUT);
}
/* Send prepared usb packet. */
if ((ret = libusb_submit_transfer(transfer)) != 0) {
sr_err("Failed to submit transfer: %s.",
libusb_error_name(ret));
devc->status = H4032L_STATUS_IDLE;
}
} else {
sr_dbg("Now idle.");
}
if (devc->status == H4032L_STATUS_IDLE)
libusb_free_transfer(transfer);
}
uint16_t h4032l_voltage2pwm(double voltage)
{
/*
* word PwmA - channel A Vref PWM value, pseudocode:
* -6V < ThresholdVoltage < +6V
* Vref = 1.8 - ThresholdVoltage
* if Vref > 10.0
* Vref = 10.0
* if Vref < -5.0
* Vref = -5.0
* pwm = ToInt((Vref + 5.0) / 15.0 * 4096.0)
* if pwm > 4095
* pwm = 4095
*/
voltage = 1.8 - voltage;
if (voltage > 10.0)
voltage = 10.0;
else if (voltage < -5.0)
voltage = -5.0;
return (uint16_t) ((voltage + 5.0) * (4096.0 / 15.0));
}
SR_PRIV int h4032l_start(const struct sr_dev_inst *sdi)
{
struct dev_context *devc = sdi->priv;
struct sr_usb_dev_inst *usb = sdi->conn;
struct libusb_transfer *transfer;
int ret;
/* Send configure command to arm the logic analyzer. */
devc->cmd_pkt.cmd = CMD_CONFIGURE;
devc->status = H4032L_STATUS_CMD_CONFIGURE;
devc->remaining_samples = devc->cmd_pkt.sample_size;
transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, usb->devhdl,
2 | LIBUSB_ENDPOINT_OUT, (unsigned char *)&devc->cmd_pkt,
sizeof(struct h4032l_cmd_pkt), h4032l_usb_callback,
(void *)sdi, H4032L_USB_TIMEOUT);
if ((ret = libusb_submit_transfer(transfer)) != 0) {
sr_err("Failed to submit transfer: %s.", libusb_error_name(ret));
libusb_free_transfer(transfer);
return SR_ERR;
}
std_session_send_df_header(sdi);
return SR_OK;
}
SR_PRIV int h4032l_dev_open(struct sr_dev_inst *sdi)
{
struct drv_context *drvc = sdi->driver->context;
struct sr_usb_dev_inst *usb = sdi->conn;
struct libusb_device_descriptor des;
libusb_device **devlist;
int ret = SR_ERR, i, device_count;
char connection_id[64];
device_count = libusb_get_device_list(drvc->sr_ctx->libusb_ctx, &devlist);
if (device_count < 0) {
sr_err("Failed to get device list: %s.",
libusb_error_name(device_count));
return SR_ERR;
}
for (i = 0; i < device_count; i++) {
libusb_get_device_descriptor(devlist[i], &des);
if (des.idVendor != H4032L_USB_VENDOR ||
des.idProduct != H4032L_USB_PRODUCT)
continue;
if ((sdi->status == SR_ST_INITIALIZING) ||
(sdi->status == SR_ST_INACTIVE)) {
/* Check device by its physical USB bus/port address. */
usb_get_port_path(devlist[i], connection_id,
sizeof(connection_id));
if (strcmp(sdi->connection_id, connection_id))
/* This is not the one. */
continue;
}
if (!(ret = libusb_open(devlist[i], &usb->devhdl))) {
if (usb->address == 0xff)
/*
* First time we touch this device after FW
* upload, so we don't know the address yet.
*/
usb->address =
libusb_get_device_address(devlist[i]);
} else {
sr_err("Failed to open device: %s.",
libusb_error_name(ret));
ret = SR_ERR;
break;
}
ret = SR_OK;
break;
}
libusb_free_device_list(devlist, 1);
return ret;
} }
/* /*
* This file is part of the libsigrok project. * This file is part of the libsigrok project.
* *
* Copyright (C) 2016 Andreas Zschunke <andreasz77@gmx.net> * Copyright (C) 2016 Andreas Zschunke <andreas.zschunke@gmx.net>
* Copyright (C) 2017 Andrej Valek <andy@skyrain.eu>
* Copyright (C) 2017 Uwe Hermann <uwe@hermann-uwe.de>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -22,23 +24,111 @@ ...@@ -22,23 +24,111 @@
#include <stdint.h> #include <stdint.h>
#include <glib.h> #include <glib.h>
#include <string.h>
#include <libsigrok/libsigrok.h> #include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h" #include "libsigrok-internal.h"
#define LOG_PREFIX "hantek-4032l" #define LOG_PREFIX "hantek-4032l"
/** Private, per-device-instance driver context. */ #define H4032L_USB_VENDOR 0x04b5
struct dev_context { #define H4032L_USB_PRODUCT 0x4032
/* Model-specific information */
#define H4032L_CMD_PKT_MAGIC 0x017f
#define H4032L_STATUS_PACKET_MAGIC 0x2B1A037F
#define H4032L_START_PACKET_MAGIC 0x2B1A027F
#define H4032L_END_PACKET_MAGIC 0x4D3C037F
enum h4032l_trigger_edge_type {
H4032L_TRIGGER_EDGE_TYPE_RISE = 0,
H4032L_TRIGGER_EDGE_TYPE_FALL,
H4032L_TRIGGER_EDGE_TYPE_TOGGLE,
H4032L_TRIGGER_EDGE_TYPE_DISABLED
};
enum h4032l_trigger_data_range_type {
H4032L_TRIGGER_DATA_RANGE_TYPE_MAX = 0,
H4032L_TRIGGER_DATA_RANGE_TYPE_MIN_OR_MAX,
H4032L_TRIGGER_DATA_RANGE_TYPE_OUT_OF_RANGE,
H4032L_TRIGGER_DATA_RANGE_TYPE_WITHIN_RANGE
};
enum h4032l_trigger_time_range_type {
H4032L_TRIGGER_TIME_RANGE_TYPE_MAX = 0,
H4032L_TRIGGER_TIME_RANGE_TYPE_MIN_OR_MAX,
H4032L_TRIGGER_TIME_RANGE_TYPE_OUT_OF_RANGE,
H4032L_TRIGGER_TIME_RANGE_TYPE_WITHIN_RANGE
};
/* Acquisition settings */ enum h4032l_trigger_data_selection {
H4032L_TRIGGER_DATA_SELECTION_NEXT = 0,
H4032L_TRIGGER_DATA_SELECTION_CURRENT,
H4032L_TRIGGER_DATA_SELECTION_PREV
};
/* Operational state */ enum h4032l_status {
H4032L_STATUS_IDLE,
H4032L_STATUS_CMD_CONFIGURE,
H4032L_STATUS_CMD_STATUS,
H4032L_STATUS_RESPONSE_STATUS,
H4032L_STATUS_RESPONSE_STATUS_RETRY,
H4032L_STATUS_RESPONSE_STATUS_CONTINUE,
H4032L_STATUS_CMD_GET,
H4032L_STATUS_FIRST_TRANSFER,
H4032L_STATUS_TRANSFER,
};
/* Temporary state across callbacks */ struct __attribute__((__packed__)) h4032l_trigger {
struct {
uint32_t edge_signal:5;
uint32_t edge_type:2;
uint32_t :1;
uint32_t data_range_type:2;
uint32_t time_range_type:2;
uint32_t data_range_enabled:1;
uint32_t time_range_enabled:1;
uint32_t :2;
uint32_t data_sel:2;
uint32_t combined_enabled:1;
} flags;
uint32_t data_range_min;
uint32_t data_range_max;
uint32_t time_range_min;
uint32_t time_range_max;
uint32_t data_range_mask;
uint32_t combine_mask;
uint32_t combine_data;
};
struct __attribute__((__packed__)) h4032l_cmd_pkt {
uint16_t magic; /* 0x017f */
uint8_t sample_rate;
struct {
uint8_t enable_trigger1:1;
uint8_t enable_trigger2:1;
uint8_t trigger_and_logic:1;
} trig_flags;
uint16_t pwm_a;
uint16_t pwm_b;
uint16_t reserved;
uint32_t sample_size; /* Sample depth in bits per channel, 2k-64M, must be multiple of 512. */
uint32_t pre_trigger_size; /* Pretrigger buffer depth in bits, must be < sample_size. */
struct h4032l_trigger trigger[2];
uint16_t cmd;
};
struct dev_context {
enum h4032l_status status;
uint32_t remaining_samples;
struct h4032l_cmd_pkt cmd_pkt;
struct libusb_transfer *usb_transfer;
uint8_t buffer[512];
uint64_t capture_ratio;
}; };
SR_PRIV int hantek_4032l_receive_data(int fd, int revents, void *cb_data); SR_PRIV int h4032l_receive_data(int fd, int revents, void *cb_data);
SR_PRIV uint16_t h4032l_voltage2pwm(double voltage);
SR_PRIV void LIBUSB_CALL h4032l_usb_callback(struct libusb_transfer *transfer);
SR_PRIV int h4032l_start(const struct sr_dev_inst *sdi);
SR_PRIV int h4032l_dev_open(struct sr_dev_inst *sdi);
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment