Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F11652376
rf_debug.c
kjopek_gmail.com (Konrad Sewiłło-Jopek)
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
kjopek_gmail.com
Nov 11 2020, 10:09 AM
2020-11-11 10:09:08 (UTC+0)
Size
10 KB
Referenced Files
None
Subscribers
None
rf_debug.c
View Options
/*
* This file contains typical path to check if we can communicate with BladeRF
* device: it loads fpga firmware (typically it breaks on RockPro64) and checks
* fpga firmware version.
*
* Based on libbladeRF initialization path. Wonder why firmware upload fails so
* spectacularly.
*/
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<assert.h>
#include
<err.h>
#include
<errno.h>
#include
<fcntl.h>
#include
<libusb.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<unistd.h>
#define BLADERF_SERIAL_LENGTH 33
#define BLADE_USB_CMD_QUERY_FPGA_STATUS 1
#define BLADE_USB_CMD_BEGIN_PROG 2
#define PERIPHERAL_EP_IN 0x82
#define PERIPHERAL_EP_OUT 0x02
#define PERIPHERAL_TIMEOUT_MS 500
#define CTRL_TIMEOUT_MS 1000
#define USB_IF_LEGACY_CONFIG 0
#define USB_IF_NULL 0
#define USB_IF_RF_LINK 1
#define USB_IF_SPI_FLASH 2
#define USB_IF_CONFIG 3
#define NIOS_PKT_LEGACY_MAGIC 'N'
#define NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID 12
#define NIOS_PKT_LEGACY_MODE_DEV_SHIFT 4
#define NIOS_PKT_LEGACY_DEV_CONFIG (0 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
#define NIOS_PKT_LEGACY_MODE_DIR_MASK 0xC0
#define NIOS_PKT_LEGACY_MODE_DIR_SHIFT 6
#define NIOS_PKT_LEGACY_MODE_DIR_READ (2 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
#define NIOS_PKT_LEGACY_MODE_DIR_WRITE (1 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
typedef
enum
{
USB_TARGET_DEVICE
,
USB_TARGET_INTERFACE
,
USB_TARGET_ENDPOINT
,
USB_TARGET_OTHER
}
usb_target
;
typedef
enum
{
USB_REQUEST_STANDARD
,
USB_REQUEST_CLASS
,
USB_REQUEST_VENDOR
}
usb_request
;
typedef
enum
{
USB_DIR_HOST_TO_DEVICE
=
0x00
,
USB_DIR_DEVICE_TO_HOST
=
0x80
}
usb_direction
;
struct
uart_cmd
{
uint8_t
addr
;
uint8_t
data
;
};
struct
bladerf_version
{
uint8_t
major
;
uint8_t
minor
;
uint16_t
patch
;
};
static
void
err_usb
(
const
char
*
err_msg
,
int
status
)
{
err
(
1
,
"%s: %s"
,
err_msg
,
libusb_error_name
(
status
));
}
static
void
change_setting
(
libusb_device_handle
*
handle
,
uint8_t
setting
)
{
int
status
;
status
=
libusb_set_interface_alt_setting
(
handle
,
0
,
setting
);
if
(
status
<
0
)
err_usb
(
"libusb_set_interface_alt_setting failed"
,
status
);
}
static
void
print_buf
(
const
char
*
msg
,
const
unsigned
char
*
buf
,
size_t
len
)
{
unsigned
ii
;
printf
(
"%s: "
,
msg
);
for
(
ii
=
0
;
ii
<
len
;
ii
++
)
printf
(
"%02hhx"
,
buf
[
ii
]);
printf
(
"
\n
"
);
}
static
inline
uint8_t
bm_request_type
(
usb_target
target_type
,
usb_request
req_type
,
usb_direction
direction
)
{
uint8_t
ret
=
0
;
switch
(
target_type
)
{
case
USB_TARGET_DEVICE
:
ret
|=
LIBUSB_RECIPIENT_DEVICE
;
break
;
case
USB_TARGET_INTERFACE
:
ret
|=
LIBUSB_RECIPIENT_INTERFACE
;
break
;
case
USB_TARGET_ENDPOINT
:
ret
|=
LIBUSB_RECIPIENT_ENDPOINT
;
break
;
default
:
ret
|=
LIBUSB_RECIPIENT_OTHER
;
}
switch
(
req_type
)
{
case
USB_REQUEST_STANDARD
:
ret
|=
LIBUSB_REQUEST_TYPE_STANDARD
;
break
;
case
USB_REQUEST_CLASS
:
ret
|=
LIBUSB_REQUEST_TYPE_CLASS
;
break
;
case
USB_REQUEST_VENDOR
:
ret
|=
LIBUSB_REQUEST_TYPE_VENDOR
;
break
;
}
switch
(
direction
)
{
case
USB_DIR_HOST_TO_DEVICE
:
ret
|=
LIBUSB_ENDPOINT_OUT
;
break
;
case
USB_DIR_DEVICE_TO_HOST
:
ret
|=
LIBUSB_ENDPOINT_IN
;
break
;
}
return
(
ret
);
}
/* Vendor command that gets/sets a 32-bit integer value */
static
inline
int
vendor_cmd_int
(
libusb_device_handle
*
dev
,
uint8_t
cmd
,
usb_direction
dir
,
int32_t
*
val
)
{
const
uint8_t
bm_req_type
=
bm_request_type
(
USB_TARGET_DEVICE
,
USB_REQUEST_VENDOR
,
dir
);
return
(
libusb_control_transfer
(
dev
,
bm_req_type
,
cmd
,
0
,
0
,
(
uint8_t
*
)
val
,
sizeof
(
int32_t
),
CTRL_TIMEOUT_MS
));
}
static
int
lusb_bulk_transfer
(
libusb_device_handle
*
handle
,
uint8_t
endpoint
,
void
*
buffer
,
uint32_t
buffer_len
,
uint32_t
timeout_ms
)
{
int
status
;
int
n_transferred
;
status
=
libusb_bulk_transfer
(
handle
,
endpoint
,
buffer
,
buffer_len
,
&
n_transferred
,
timeout_ms
);
printf
(
"Libusb status: %d
\n
"
,
status
);
if
(
status
==
0
&&
((
uint32_t
)
n_transferred
!=
buffer_len
))
{
printf
(
"Short bulk transfer: requested=%u, transferred=%u
\n
"
,
buffer_len
,
n_transferred
);
status
=
EIO
;
}
return
(
status
);
}
/* Stolen from libbladerf. */
static
int
nios_access
(
libusb_device_handle
*
dev
,
uint8_t
peripheral
,
usb_direction
dir
,
struct
uart_cmd
*
cmd
,
size_t
len
)
{
int
status
;
size_t
i
;
uint8_t
buf
[
16
]
=
{
0
};
const
uint8_t
pkt_mode_dir
=
(
dir
==
USB_DIR_HOST_TO_DEVICE
)
?
NIOS_PKT_LEGACY_MODE_DIR_WRITE
:
NIOS_PKT_LEGACY_MODE_DIR_READ
;
assert
(
len
<=
((
sizeof
(
buf
)
-
2
)
/
2
));
/* Populate the buffer for transfer, given address data pairs */
buf
[
0
]
=
NIOS_PKT_LEGACY_MAGIC
;
buf
[
1
]
=
pkt_mode_dir
|
peripheral
|
(
uint8_t
)
len
;
for
(
i
=
0
;
i
<
len
;
i
++
)
{
buf
[
i
*
2
+
2
]
=
cmd
[
i
].
addr
;
buf
[
i
*
2
+
3
]
=
cmd
[
i
].
data
;
}
print_buf
(
"NIOS II access request"
,
buf
,
16
);
/* Send the command */
status
=
lusb_bulk_transfer
(
dev
,
PERIPHERAL_EP_OUT
,
buf
,
sizeof
(
buf
),
PERIPHERAL_TIMEOUT_MS
);
if
(
status
!=
0
)
{
printf
(
"Failed to submit NIOS II request: %s
\n
"
,
libusb_error_name
(
status
));
return
(
status
);
}
/* Read back the ACK. The command data is only used for a read operation,
* and is thrown away otherwise */
status
=
lusb_bulk_transfer
(
dev
,
PERIPHERAL_EP_IN
,
buf
,
sizeof
(
buf
),
PERIPHERAL_TIMEOUT_MS
);
if
(
dir
==
NIOS_PKT_LEGACY_MODE_DIR_READ
&&
status
==
0
)
{
for
(
i
=
0
;
i
<
len
;
i
++
)
{
cmd
[
i
].
data
=
buf
[
i
*
2
+
3
];
}
}
if
(
status
==
0
)
{
print_buf
(
"NIOS II access response:
\n
"
,
buf
,
16
);
}
else
{
printf
(
"Failed to receive NIOS II response: %s
\n
"
,
libusb_error_name
(
status
));
}
return
(
status
);
}
/* Stolen from libbladerf. */
static
int
nios_legacy_get_fpga_version
(
libusb_device_handle
*
dev
,
struct
bladerf_version
*
ver
)
{
change_setting
(
dev
,
USB_IF_RF_LINK
);
int
i
,
status
;
struct
uart_cmd
cmd
;
for
(
i
=
0
;
i
<
4
;
i
++
)
{
cmd
.
addr
=
NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID
+
i
;
cmd
.
data
=
0xff
;
status
=
nios_access
(
dev
,
NIOS_PKT_LEGACY_DEV_CONFIG
,
USB_DIR_DEVICE_TO_HOST
,
&
cmd
,
1
);
if
(
status
!=
0
)
{
printf
(
"Failed to read FPGA version[%d]: %s (%d)
\n
"
,
i
,
libusb_error_name
(
status
),
status
);
return
(
status
);
}
switch
(
i
)
{
case
0
:
ver
->
major
=
cmd
.
data
;
break
;
case
1
:
ver
->
minor
=
cmd
.
data
;
break
;
case
2
:
ver
->
patch
=
cmd
.
data
;
break
;
case
3
:
ver
->
patch
|=
(
cmd
.
data
<<
8
);
break
;
default
:
assert
(
!
"Bug"
);
}
}
return
(
status
);
}
static
int
is_fpga_configured
(
libusb_device_handle
*
handle
)
{
int
result
=
-1
;
int
status
;
/* This environment variable provides a means to force libbladeRF to not
* attempt to access the FPGA.
*
* This provides a workaround for the situation where a user did not
* remove an FPGA in SPI flash prior to flashing new firmware and
* updating libbladeRF. Specifically, this has proven to be a problem
* with pre-v0.0.1 FPGA images, as they do not provide version readback
* functionality.
*/
status
=
vendor_cmd_int
(
handle
,
BLADE_USB_CMD_QUERY_FPGA_STATUS
,
USB_DIR_DEVICE_TO_HOST
,
&
result
);
if
(
status
<
0
)
return
(
status
);
else
if
(
result
==
0
||
result
==
1
)
return
(
result
);
assert
(
!
"BUG"
);
}
static
int
begin_fpga_programming
(
libusb_device_handle
*
handle
)
{
int
result
;
int
status
=
vendor_cmd_int
(
handle
,
BLADE_USB_CMD_BEGIN_PROG
,
USB_DIR_DEVICE_TO_HOST
,
&
result
);
if
(
status
!=
0
)
{
return
(
status
);
}
else
if
(
result
!=
0
)
{
printf
(
"Startg fpga programming, result = %d
\n
"
,
result
);
return
(
LIBUSB_ERROR_OTHER
);
}
else
{
return
(
0
);
}
}
static
int
load_fpga
(
libusb_device_handle
*
handle
,
const
uint8_t
*
image
,
size_t
image_size
)
{
unsigned
int
wait_count
;
const
unsigned
int
timeout_ms
=
(
2
*
CTRL_TIMEOUT_MS
);
int
status
;
/* Switch to the FPGA configuration interface */
change_setting
(
handle
,
USB_IF_CONFIG
);
/* Begin programming */
status
=
begin_fpga_programming
(
handle
);
if
(
status
<
0
)
{
printf
(
"Failed to initiate FPGA programming: %s (%d)
\n
"
,
libusb_error_name
(
status
),
status
);
return
(
status
);
}
/* Send the file down */
assert
(
image_size
<=
UINT32_MAX
);
status
=
lusb_bulk_transfer
(
handle
,
PERIPHERAL_EP_OUT
,
(
void
*
)
image
,
(
uint32_t
)
image_size
,
timeout_ms
);
if
(
status
<
0
)
{
printf
(
"Failed to write FPGA bitstream to FPGA: %s (%d)
\n
"
,
libusb_error_name
(
status
),
status
);
return
(
status
);
}
/* Poll FPGA status to determine if programming was a success */
wait_count
=
10
;
status
=
0
;
while
(
wait_count
>
0
&&
status
==
0
)
{
status
=
is_fpga_configured
(
handle
);
if
(
status
==
1
)
{
break
;
}
usleep
(
200000
);
wait_count
--
;
}
/* Failed to determine if FPGA is loaded */
if
(
status
<
0
)
{
printf
(
"Failed to determine if FPGA is loaded: %s (%d)
\n
"
,
libusb_error_name
(
status
),
status
);
return
(
status
);
}
else
if
(
wait_count
==
0
&&
status
!=
0
)
{
printf
(
"Timeout while waiting for FPGA configuration status
\n
"
);
return
(
LIBUSB_ERROR_TIMEOUT
);
}
return
(
0
);
}
static
void
usage
(
void
)
{
printf
(
"test_bladerf <path to firmware>
\n
"
);
}
static
int
load_firmware
(
libusb_device_handle
*
handle
,
const
char
*
path
)
{
struct
stat
stat
;
uint8_t
*
image
;
int
fd
;
fd
=
open
(
path
,
O_RDONLY
);
if
(
fd
<
0
)
err
(
1
,
"open(2) failed"
);
if
(
fstat
(
fd
,
&
stat
)
!=
0
)
err
(
1
,
"stat(2) failed"
);
image
=
calloc
(
1
,
stat
.
st_size
);
if
(
image
==
NULL
)
err
(
1
,
"calloc(2) failed"
);
if
(
read
(
fd
,
image
,
stat
.
st_size
)
!=
stat
.
st_size
)
err
(
1
,
"read(2) failed"
);
close
(
fd
);
if
(
load_fpga
(
handle
,
image
,
stat
.
st_size
)
!=
0
)
err
(
1
,
"could not load firmware"
);
free
(
image
);
return
(
0
);
}
int
main
(
int
argc
,
char
**
argv
)
{
libusb_device
*
dev
=
NULL
;
libusb_device_handle
*
handle
=
NULL
;
struct
libusb_device_descriptor
desc
;
struct
bladerf_version
ver
;
char
serial
[
BLADERF_SERIAL_LENGTH
];
int
status
;
if
(
argc
!=
2
)
{
usage
();
return
(
1
);
}
status
=
libusb_init
(
NULL
);
if
(
status
<
0
)
err_usb
(
"libusb_init failed"
,
status
);
libusb_set_debug
(
NULL
,
LIBUSB_DEBUG_FUNCTION
);
handle
=
libusb_open_device_with_vid_pid
(
NULL
,
0x2cf0
,
0x5250
);
if
(
handle
==
NULL
)
err
(
1
,
"libusb_open_device_with_vid_pid(2) failed"
);
dev
=
libusb_get_device
(
handle
);
if
(
dev
==
NULL
)
err
(
1
,
"libusb_get_device failed"
);
status
=
libusb_claim_interface
(
handle
,
0
);
if
(
status
<
0
)
err_usb
(
"libusb_claim_interface failed"
,
status
);
status
=
libusb_get_device_descriptor
(
dev
,
&
desc
);
if
(
status
!=
0
)
err_usb
(
"libusb_get_device_descriptor failed"
,
status
);
status
=
libusb_get_string_descriptor_ascii
(
handle
,
desc
.
iSerialNumber
,
(
unsigned
char
*
)
serial
,
BLADERF_SERIAL_LENGTH
);
if
(
status
<
0
)
err_usb
(
"libusb_get_string_descriptor_ascii failed"
,
status
);
serial
[
BLADERF_SERIAL_LENGTH
-1
]
=
'\0'
;
printf
(
"Serial: %s
\n
"
,
serial
);
load_firmware
(
handle
,
argv
[
1
]);
change_setting
(
handle
,
USB_IF_CONFIG
);
memset
(
&
ver
,
0
,
sizeof
(
ver
));
nios_legacy_get_fpga_version
(
handle
,
&
ver
);
printf
(
"Version: %hhu.%hhu.%hu
\n
"
,
ver
.
major
,
ver
.
minor
,
ver
.
patch
);
/* Configure known state before leave. */
change_setting
(
handle
,
USB_IF_NULL
);
libusb_close
(
handle
);
libusb_exit
(
NULL
);
return
(
0
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3082887
Default Alt Text
rf_debug.c (10 KB)
Attached To
Mode
D27174: Allow user to configure endpoints twice
Attached
Detach File
Event Timeline
Log In to Comment