Esempio n. 1
0
def main():

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description=
        "Utility for loading runtime extensions on to a GreatFET board.")
    parser.add_argument(
        '--m0',
        dest="m0",
        type=argparse.FileType('rb'),
        metavar='<filename>',
        help="loads the provided loadable file to run on the GreatFET's m0 core"
    )

    args = parser.parse_args()
    log_function = parser.get_log_function()
    device = parser.find_specified_device()

    if not args.m0:
        parser.print_help()
        sys.exit(-1)

    if args.m0:
        data = args.m0.read()

        log_function(
            "Loading {} byte loadable onto the M0 coprocessor.\n".format(
                len(data)))
        device.m0.run_loadable(data)
def main():
    logfile = 'log.bin'
#    logfile = '/tmp/fifo'
    from greatfet.utils import GreatFETArgumentParser
   
    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(description="Utility for experimenting with GreatFET's ADC")
    parser.add_argument('-f', dest='filename', metavar='<filename>', type=str, help="Write data to file", default=logfile)
    parser.add_argument('-a', dest='adc', action='store_true', help="Use internal ADC")

    args = parser.parse_args()
    log_function = parser.get_log_function()
    device = parser.find_specified_device()

    if args.adc:
        device.comms._vendor_request_out(vendor_requests.ADC_INIT)
    else:
        device.comms._vendor_request_out(vendor_requests.SDIR_RX_START)

    time.sleep(1)
    print(device.comms.device)

    with open(args.filename, 'wb') as f:
        try:
            while True:
                d = device.comms.device.read(0x81, 0x4000, 1000)
                # print(d)
                f.write(d)
        except KeyboardInterrupt:
            pass

    if not args.adc:
        device.comms._vendor_request_out(vendor_requests.SDIR_RX_STOP)
Esempio n. 3
0
def main():

    commands = {'scan': print_chain_info, 'svf': play_svf_file}

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Utility for working with JTAG devices")
    parser.add_argument('command',
                        choices=commands,
                        help='the operation to complete')
    parser.add_argument('filename',
                        metavar="[filename]",
                        nargs='?',
                        help='the filename to read from, for SVF playback')

    args = parser.parse_args()
    device = parser.find_specified_device()

    if args.command == 'scan':
        args.verbose = True
    elif args.filename == "-":
        args.verbose = False

    # Grab our log functions.
    log_function, log_error = parser.get_log_functions()

    # Execute the relevant command.
    command = commands[args.command]
    command(device.jtag, log_function, log_error, args)
Esempio n. 4
0
def main():
    from greatfet.utils import GreatFETArgumentParser

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(description="utility for reading from the GreatFET's ADC")
    parser.add_argument('-f', '--format', dest='format', type=str, default='voltage',
                        choices=['voltage', 'raw'],
                        help="Format to output in.\nVoltage string, or raw fraction returned by the ADC.")
    parser.add_argument('-m', '--machine-readable', dest='machine_readable', action='store_true',
                        default=False, help="Don't output unit suffixes.")
    parser.add_argument('-n', '--samples', dest='sample_count', type=int, default=1,
                        help="The number of samples to read. (default: 1)")
    parser.add_argument('-o', '--output', dest='output', type=argparse.FileType('w'), default='-',
                        help="File to output to. Specify - to output to stdout (default).")

    args         = parser.parse_args()
    log_function = parser.get_log_function()
    device       = parser.find_specified_device()

    if not device.supports_api('adc'):
        sys.stderr.write("This device doesn't seem to support an ADC. Perhaps your firmware needs to be upgraded?\n")
        sys.exit(0)

    samples = device.adc.read_samples(args.sample_count)

    for sample in samples:
        output_sample(sample, args)

    args.output.close()
def main():


    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Convenience shell for working with GreatFET devices.")
    parser.add_argument('-e', '--exec', metavar="code", type=str, help="Executes the provided code as though it were passed" +
            "to a greatfet shell, and then terminates.", dest="code")

    args = parser.parse_args()
    gf = parser.find_specified_device()

    # Handle any inline execution.
    if args.code:

        # Replace any ;'s with newlines, so we can execute more than one statement.
        code = re.sub(";\s*", "\n", args.code)
        lines = code.split("\n")

        # And execute the code.
        for line in lines:
            result = repr(eval(line))

        # Print the last result and return.
        print(result)
        sys.exit(0)


    # Break into IPython for the shell.
    print("Spwaning an IPython shell for easy access to your GreatFET.")
    print("Like normal python, you can use help(object) to get help for that object.")
    print("Try help(gf.apis.example) to see the documentation for the example API.\n")

    print("A GreatFET object has been created for you as 'gf'. Have fun!\n")
    IPython.start_ipython(user_ns={"gf": gf}, display_banner=False, argv=[])
Esempio n. 6
0
def main():
    global gf

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Runner for the GreatFET board self-tests.")

    # Parse the arguments, and connect to the relevant GreatFET.
    gf = parser.find_specified_device()

    # TODO: possibly reset the GreatFET _first_, to ensure it's in a known state?

    # Run our self-tests.
    unittest.main(verbosity=2)
Esempio n. 7
0
def main():
    global gf, be_suspicious

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(description="Runner for the GreatFET board self-tests.")
    parser.add_argument('--suspicious', action='store_true', help="Be extra suspicious; fail on things that aren't usually issues.")

    # Parse the arguments, and connect to the relevant GreatFET.
    args, remaining = parser.parse_known_args()
    gf = parser.find_specified_device()

    be_suspicious = args.suspicious

    # TODO: possibly reset the GreatFET _first_, to ensure int's in a known state?

    # Run our self-tests.
    unittest.main(module=__name__, verbosity=2 if args.verbose else 1, warnings='ignore', argv=[sys.argv[0]] + remaining)
Esempio n. 8
0
def main():
    from greatfet.utils import GreatFETArgumentParser

    supported_interfaces = {'spi': lambda d: d.spi, 'i2c': lambda d: d.i2c}
    supported_interface_string = ', '.join(supported_interfaces)

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="bus pirate emulation utility for GreatFET",
        verbose_by_default=True)
    parser.add_argument(
        'interface',
        nargs='?',
        help=
        "The type of interface to use for our commands. Currently supported: {}"
        .format(supported_interface_string))
    parser.add_argument('commands',
                        nargs='*',
                        default=[],
                        help="Bus pirate command to execute.")
    args = parser.parse_args()

    log_function, log_error = parser.get_log_functions()
    device = parser.find_specified_device()

    # By default, if we have a command on the command line, run non-interactively.
    if args.commands:
        interface = supported_interfaces[args.interface](device)
        commands = ' '.join(args.commands)

        # Print the executed commands, and their results.
        log_function("> {}".format(commands))
        run_batch(interface, commands, log_function)
    else:
        if args.interface:
            interface = supported_interfaces[args.interface](device)
        else:
            interface = None

            # FIXME: remove this when we support proper mode-switching
            log_error(
                "Mode-switching currently not supported from interactive mode. Specify a mode on the command line."
            )
            sys.exit(-1)

        run_interactive(interface, print, args.commands)
Esempio n. 9
0
def main():

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(description="""Utility for chipcon debugging via GreatFET
                                                    (See /firmware/common/swra.c for pin mappings)""",
                                    verbose_by_default=True)
    parser.add_argument('--chip-id', action='store_true', # Short options (one dash) should always be one letter
                        help="Print the chip ID of the connected device.")
    parser.add_argument('-a', '--address', dest='address', metavar='<n>', type=int_auto_base,
                        help="Starting address (default: 0)", default=0)
    parser.add_argument('-l', '--length', dest='length', metavar='<n>', type=int_auto_base,
                        help="Length of data to read")
    parser.add_argument('-r', '--read', metavar='<filename>', type=argparse.FileType('wb'),
                        help="Read data into file")
    parser.add_argument('--no-erase', dest='erase', default=True, action='store_false',
                        help="Do not erase the flash before performing a write operation")
    parser.add_argument('--no-verify', dest='verify', action='store_false', default=True,
                        help="Do not verify the flash after performing a write operation")
    parser.add_argument('-E', '--mass-erase', action='store_true', help="Erase the entire flash memory")
    parser.add_argument('-w', '--write', metavar='<filename>', type=argparse.FileType('rb'),
                        help="Write data from file")

    args = parser.parse_args()

    log_function = log_verbose if args.verbose else log_silent

    device = parser.find_specified_device()

    chipcon = device.create_programmer('chipcon')

    chipcon.debug_init()

    if args.chip_id:
        chip_id(chipcon)

    if args.read:
        if not args.length:
            parser.error("argument -s/--length: expected one argument")
        read_flash(chipcon, args.read, args.address, args.length, log_function)

    if args.mass_erase:
        mass_erase_flash(chipcon, log_function)

    if args.write:
        program_flash(chipcon, args.write, args.address, args.erase, args.verify, log_function)
Esempio n. 10
0
def main():

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Convenience shell for working with GreatFET devices.")
    gf = parser.find_specified_device()

    # Break into IPython for the shell.
    print("Spwaning an IPython shell for easy access to your GreatFET.")
    print(
        "Like normal python, you can use help(object) to get help for that object."
    )
    print(
        "Try help(gf.apis.example) to see the documentation for the example API.\n"
    )

    print("A GreatFET object has been created for you as 'gf'. Have fun!\n")
    IPython.start_ipython(user_ns={"gf": gf}, display_banner=False)
Esempio n. 11
0
def main():
    from greatfet.utils import GreatFETArgumentParser

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Utility for experimenting with GreatFET's DAC",
        verbose_by_default=True)
    parser.add_argument(
        '-f',
        '--format',
        dest='format',
        type=str,
        default='voltage',
        choices=['voltage', 'raw'],
        help=
        "Format for the input.\nVoltage string, or binary value to be loaded into the DAC."
    )
    parser.add_argument(
        'value',
        metavar='[value]',
        type=float,
        help=
        "The desired voltage (default) or raw value to load into DAC (with -f raw)."
    )

    args = parser.parse_args()
    log_function = parser.get_log_function()
    device = parser.find_specified_device()

    device.apis.dac.initialize()

    if args.format == "voltage":

        # Voltage must be passed to the device in millivolts, so * 1000.
        device.apis.dac.set_voltage(int(args.value * 1000))
        log_function("DAC set to {} volts".format(args.value))

    else:

        device.apis.dac.set_value(int(args.value))
        log_function("DAC set to {}".format(int(args.value)))
Esempio n. 12
0
def main():
    parser = GreatFETArgumentParser(
        description="Convenience shell for working with GreatFET devices.")
    parser.add_argument('-n',
                        '--length',
                        dest='length',
                        metavar='<bytes>',
                        type=int,
                        help="maximum amount of data to read (default: 4096)",
                        default=4096)
    args = parser.parse_args()

    args = parser.parse_args()
    gf = parser.find_specified_device()
    log_function = parser.get_log_function()

    #Read and print the logs
    logs = gf.read_debug_ring(args.length)

    log_function("Ring buffer contained {} bytes of data:\n".format(len(logs)))
    print(logs)
Esempio n. 13
0
def main():
    from greatfet.utils import GreatFETArgumentParser
   
    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(description="Periodically print temperature from DS18B20 sensor")
    parser.add_argument('-S', dest='s20', action='store_true', help='DS18S20')

    args = parser.parse_args()
    log_function = parser.get_log_function()
    device = parser.find_specified_device()

    while True:
        data = device.vendor_request_in(vendor_requests.DS18B20_READ, length=2, timeout=2000)
        # temperature data is 16 bit signed
        temp = struct.unpack('<h', data)[0]
        if args.s20:
            temp /= 2.0
        else:
            temp /= 16.0
        print(time.strftime("%H:%M:%S"), temp, '{:.01f}'.format(temp * 9 / 5 + 32))
        time.sleep(1)
def main():
    parser = GreatFETArgumentParser(
        description="Convenience shell for working with GreatFET devices.")
    parser.add_argument('-n', '--length', dest='length', metavar='<bytes>', type=int,
                        help="maximum amount of data to read (default: 4096)", default=4096)
    args = parser.parse_args()


    args = parser.parse_args()
    gf = parser.find_specified_device()
    log_function = parser.get_log_function()

    if not gf.supports_api('debug'):
        print("ERROR: This board doesn't appear to support the debug API -- was its firmware built without it?")
        return

    if not gf.apis.debug.supports_verb('read_dmesg'):
        print("ERROR: This board doesn't appear to support debug logging. Was its firmware built with logging or the ringbuffer off?")
        return

    #Read and print the dmesg log.
    logs = gf.read_debug_ring(args.length)
    log_function("Ring buffer contained {} bytes of data:\n".format(len(logs)))
    print(logs)
Esempio n. 15
0
def main():

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Convenience shell for working with GreatFET devices.")
    parser.add_argument(
        '-e',
        '--exec',
        metavar="code",
        type=str,
        help="Executes the provided code as though it were passed" +
        "to a greatfet shell, and then terminates.",
        dest="code")

    args = parser.parse_args()
    gf = parser.find_specified_device()

    # Handle any inline execution.
    if args.code:

        # Replace any ;'s with newlines, so we can execute more than one statement.
        code = re.sub(";\s*", "\n", args.code)
        lines = code.split("\n")

        # And execute the code.
        for line in lines:
            result = repr(eval(line))

        # Print the last result and return.
        print(result)
        sys.exit(0)

    # Break into IPython for the shell.
    #print("Spwaning an IPython shell for easy access to your GreatFET.")
    #print("Like normal python, you can use help(object) to get help for that object.")
    #print("Try help(gf.apis.example) to see the documentation for the example API.\n")
    #print("A GreatFET object has been created for you as 'gf'. Have fun!\n")
    #IPython.start_ipython(user_ns={"gf": gf}, display_banner=False, argv=[])

    #set up GPIO pins as outputs
    pin_clk = gf.gpio.get_pin('J1_P16')
    pin_sleep = gf.gpio.get_pin('J1_P8')
    #pin_cmode = gf.gpio.get_pin('J1_P14')
    #pin_mode = gf.gpio.get_pin('J1_P19')

    pin0 = gf.gpio.get_pin('J1_P15')  #LSB
    pin1 = gf.gpio.get_pin('J1_P18')
    pin2 = gf.gpio.get_pin('J1_P17')
    pin3 = gf.gpio.get_pin('J1_P20')
    pin4 = gf.gpio.get_pin('J1_P22')
    pin5 = gf.gpio.get_pin('J1_P21')
    pin6 = gf.gpio.get_pin('J1_P26')
    pin7 = gf.gpio.get_pin('J1_P25')  #MSB

    pin_clk.set_direction(1)
    pin_sleep.set_direction(1)
    #pin_cmode.set_direction(1)
    #pin_mode.set_direction(1)
    pin0.set_direction(1)
    pin1.set_direction(1)
    pin2.set_direction(1)
    pin3.set_direction(1)
    pin4.set_direction(1)
    pin5.set_direction(1)
    pin6.set_direction(1)
    pin7.set_direction(1)

    #initialize pins
    pin_sleep.write(0)
    pin_clk.write(0)
    #pin_cmode.write(0)
    #pin_mode.write(0)

    val = 0

    pin0.write(val)
    pin1.write(val)
    pin2.write(val)
    pin3.write(val)
    pin4.write(val)
    pin5.write(val)
    pin6.write(val)
    pin7.write(val)

    #clock pulse function
    def clock_pulse():
        pin_clk.write(0)
        #time.sleep(0.1)
        pin_clk.write(1)
        #time.sleep(0.1)
        pin_clk.write(0)

    def write_pin(pin, value):
        pin.write(value)

    #why do we need two clock pulses to get correct output? There can be a reset button between pulses and it stills works afterwards
    write_pin(pin7, 0)
    clock_pulse()
    clock_pulse()

    while False:
        pin7.write(1)
        clock_pulse()
        time.sleep(2)
        pin7.write(0)
        clock_pulse()
        time.sleep(2)
Esempio n. 16
0
def main():

    # Grab any GreatFET assets that should have shipped with the tool.
    dfu_stub_path = find_greatfet_asset('flash_stub.bin')
    auto_firmware_path = find_greatfet_asset("greatfet_usb.bin")

    # Set up a simple argument parser.-
    parser = GreatFETArgumentParser(
        dfu=True,
        verbose_by_default=True,
        description="Utility for flashing firmware on GreatFET boards")
    parser.add_argument('-a',
                        '--address',
                        metavar='<n>',
                        type=int,
                        help="starting address (default: 0)",
                        default=0)
    parser.add_argument(
        '-l',
        '--length',
        metavar='<n>',
        type=int,
        default=None,
        help=
        "number of bytes to read; if not specified, we try to read the programmed sections"
    )
    parser.add_argument('-r',
                        '--read',
                        dest='read',
                        metavar='<filename>',
                        type=str,
                        help="Read data into file",
                        default='')
    parser.add_argument('-w',
                        '--write',
                        dest='write',
                        metavar='<filename>',
                        type=str,
                        help="Write data from file",
                        default='')
    parser.add_argument(
        '-R',
        '--reset',
        dest='reset',
        action='store_true',
        help="Reset GreatFET after performing other operations.")
    parser.add_argument(
        '-V',
        '--volatile-upload',
        dest='volatile',
        metavar="<filename>",
        type=str,
        help=
        "Uploads a GreatFET firmware image to RAM via DFU mode. Firmware is not flashed."
    )

    # If we have the ability to automatically install firmware, provide that as an option.
    if auto_firmware_path:
        parser.add_argument(
            '--autoflash',
            action='store_true',
            dest='autoflash',
            help=
            "Automatically flash the attached board with the firmware corresponding to the installed tools."
        )
        parser.add_argument(
            '-U',
            '--volatile-upload-auto',
            dest='volatile_auto',
            action='store_true',
            help=
            "Automatically upload the tools' firmware via DFU mode. Firmware is not flashed."
        )

    args = parser.parse_args()

    # If we're trying to automatically flash the given firmware, set the relevant options accordingly.
    try:
        if not args.write and args.autoflash:
            args.write = auto_firmware_path
            args.reset = True
    except AttributeError:
        pass

    try:
        if not args.volatile and args.volatile_auto:
            args.volatile = auto_firmware_path
    except AttributeError:
        pass

    # Validate our options.

    # If we don't have an option, print our usage.
    if not any((args.read, args.write, args.reset, args.volatile)):
        parser.print_help()
        sys.exit(0)

    # Determine whether we're going to log to the stdout, or not at all.
    log_function = parser.get_log_function()

    if args.dfu_stub:
        dfu_stub_path = args.dfu_stub

    # If we're uploading a file via DFU for a "volatile" flash, do so and abort.
    if args.volatile:

        try:
            device = LPC43xxTarget()
        except BoardNotFoundError:
            print("Couldn't find a GreatFET-compatible board in DFU mode!",
                  file=sys.stderr)
            sys.exit(errno.ENODEV)

        log_function("Uploading data to RAM...\n")
        dfu_upload(device, args.volatile, log_function)
        sys.exit(0)

    # If we're supposed to install firmware via a DFU stub, install it first.
    if args.dfu:
        try:
            load_dfu_stub(dfu_stub_path)
        except DeviceNotFoundError:
            print("Couldn't find a GreatFET-compatible board in DFU mode!",
                  file=sys.stderr)
            sys.exit(errno.ENODEV)

    # Create our GreatFET connection.
    log_function("Trying to find a GreatFET device...")
    device = parser.find_specified_device()
    log_function("{} found. (Serial number: {})".format(
        device.board_name(), device.serial_number()))

    # Ensure that the device supports an onboard SPI flash.
    try:
        device.onboard_flash
    except AttributeError:
        print(
            "The attached GreatFET ({}) doesn't appear to have an SPI flash to program!"
            .format(device.board_name()),
            file=sys.stderr)
        sys.exit(errno.ENOSYS)

    # If we have a write command, write first, to match the behavior of hackrf_spiflash.
    if args.write:
        log_function("Writing data to SPI flash...\n")
        spi_flash_write(device, args.write, args.address, log_function)
        log_function("Write complete!")
        if not (args.reset or args.dfu):
            log_function(
                "Reset not specified; new firmware will not start until next reset."
            )

    # Handle any read commands.
    if args.read:
        log_function("Reading data from SPI flash...\n")
        spi_flash_read(device, args.read, args.address, args.length,
                       log_function)
        log_function("Read complete!")

    # Finally, reset the target
    if args.reset or args.dfu:
        log_function("Resetting GreatFET...")
        device.reset(reconnect=False, is_post_firmware_flash=bool(args.write))
        log_function("Reset complete!")
Esempio n. 17
0
def main():
    """ Core command. """

    global input_thread, termination_request, console

    parity_modes = {
        'none': UART.PARITY_NONE,
        'odd': UART.PARITY_ODD,
        'even': UART.PARITY_EVEN,
        'one': UART.PARITY_STUCK_AT_ONE,
        'zero': UART.PARITY_STUCK_AT_ZERO
    }

    # Set up a simple argument parser.
    # TODO: support configurations such as '8n1'
    parser = GreatFETArgumentParser(
        description="Simple GreatFET UART monitor.")
    parser.add_argument(
        'baud',
        nargs='?',
        type=from_eng_notation,
        default=115200,
        help="Baud rate; in symbols/second. Defaults to 115200.")
    parser.add_argument('-d',
                        '--data',
                        type=int,
                        default=8,
                        help="The number of data bits per frame.")
    parser.add_argument('-S',
                        '--stop',
                        type=int,
                        default=1,
                        help="The number of stop bits per frame.")
    parser.add_argument('-P',
                        '--parity',
                        choices=parity_modes,
                        default=0,
                        help="The type of parity to use.")
    parser.add_argument('-E',
                        '--echo',
                        action='store_true',
                        help="If provided, local echo will be enabled.")
    parser.add_argument(
        '-N',
        '--no-newline-translation',
        action='store_false',
        dest='tr_newlines',
        help="Provide this option to disable newline translation.")

    args = parser.parse_args()
    device = parser.find_specified_device()

    # Grab our log functions.
    log_function, log_error = parser.get_log_functions()

    # Configure our UART.
    if not hasattr(device, 'uart'):
        log_error(
            "This device doesn't appear to support the UART API. Perhaps it needs a firmware upgrade?"
        )
        sys.exit(-1)

    # Notify the user that we're entering monitor mode.
    log_function(
        "Entering monitor mode. To terminate, type CTRL+A, then CTRL+C.")

    # Create a console object.
    console = Console()
    console.setup()

    # Create a thread to capture input data into a locally-processed queue.
    input_queue = queue.Queue()
    termination_request = threading.Event()
    input_thread = threading.Thread(target=input_handler,
                                    args=(console, input_queue,
                                          termination_request))
    input_thread.start()

    # Configure our UART parameters.
    device.uart.update_parameters(baud=args.baud,
                                  data_bits=args.data,
                                  stop_bits=args.stop,
                                  parity=args.parity)

    # Generate our UART monitor.
    while True:

        # Grab any data from the serial port, and print it to the screen.
        data = device.uart.read()

        # If we're preforming newline translation, prepend a "\r" to any newline.
        if args.tr_newlines and (data == b"\n"):
            console.write_bytes(b"\r")

        # Stick the UART data onscreen.
        console.write_bytes(data)

        # Grab any data from the user, and send it via serial.
        try:
            new_key = input_queue.get_nowait()
            handle_special_functions(new_key)

            # If local echo is on, print the character to our local console.
            if args.echo:
                sys.stdout.buffer.write(new_key)

            if args.tr_newlines and (new_key == b"\n"):
                device.uart.write(b"\r")

            device.uart.write(new_key)
        except queue.Empty:
            pass
Esempio n. 18
0
def main():

    # Simple type-arguments for parsing.
    int_from_msps = lambda x: from_eng_notation(
        x, units=['Hz', 'SPS'], to_type=int)

    # Set up our argument parser.
    parser = GreatFETArgumentParser(
        description="Logic analyzer implementation for GreatFET",
        verbose_by_default=True)
    parser.add_argument(
        '-o',
        '-b',
        '--binary',
        dest='binary',
        metavar='<filename>',
        type=str,
        help="Write the raw samples captured to a file with the provided name."
    )
    parser.add_argument(
        '-p',
        '--pulseview',
        '--sigrok',
        dest='pulseview',
        metavar="<filename>",
        type=str,
        help=
        "Generate a Sigrok/PulseView session file, and write it to the provided filename."
    )
    parser.add_argument('-f',
                        '--samplerate',
                        metavar='samples_per_second',
                        type=int_from_msps,
                        default=17000000,
                        dest='sample_rate',
                        help='samples to capture per second')
    parser.add_argument('-n',
                        '--num-channels',
                        metavar='channels',
                        type=int,
                        default=8,
                        dest='bus_width',
                        help='the number of channels to capture')
    parser.add_argument(
        '-B',
        '--second-bank',
        action='store_const',
        const=8,
        default=0,
        dest='first_pin',
        help=
        "Provide this option to capture from SGPIO8 up, rather than from SGPIO0 up."
    )
    parser.add_argument(
        '-O',
        '--stdout',
        dest='write_to_stdout',
        action='store_true',
        help=
        'Provide this option to write the raw binary samples to the standard out. Implies -q.'
    )
    parser.add_argument(
        '--rhododendron',
        dest='rhododendron',
        action='store_true',
        help=
        'Capture raw packets for e.g. low or full speed USB using the Rhodadendon neighbor.'
    )
    parser.add_argument(
        '--raw-usb',
        dest='raw_usb',
        action='store_true',
        help=
        'Capture raw packets for e.g. low or full speed USB on SGPIO0 and SGPIO1.'
    )
    parser.add_argument(
        '--debug-sgpio',
        dest='debug_sgpio',
        action='store_true',
        help=
        'Developer option for debugging; dumps the SGPIO configuration before starting.'
    )
    parser.add_argument(
        '--stats',
        dest='print_stats',
        action='store_true',
        help='Print capture statistics after the transfer is complete.')

    # And grab our GreatFET.
    args = parser.parse_args()

    # If we're writing binary samples directly to stdout, don't emit logs to stdout; otherwise, honor the
    # --quiet flag.
    if args.write_to_stdout:
        log_function = log_silent
    else:
        log_function = parser.get_log_function()

    # Ensure we have at least one write operation.
    if not (args.pulseview or args.binary or args.write_to_stdout):
        parser.print_help()
        sys.exit(-1)

    # Capture a few of the arguments.
    sample_rate = args.sample_rate
    bus_width = args.bus_width

    channel_names = None

    # If we have one of the raw-usb options, apply their settings.
    if args.raw_usb or args.rhododendron:
        sample_rate = int(51e6)
        bus_width = 2
        channel_names = {0: 'D-', 1: 'D+'}

    if args.rhododendron:
        args.first_pin = 8

    # Find our GreatFET.
    device = parser.find_specified_device()

    # Configure which locations we're using for SGPIO8/9.
    device.apis.logic_analyzer.configure_alt_mappings(args.rhododendron)

    # Set the first pin in our capture according to our bank setting.
    device.apis.logic_analyzer.change_first_pin(args.first_pin)

    # Replace the sample rate with the actual achieved sample rate.
    sample_rate, buffer_size, endpoint = device.apis.logic_analyzer.configure(
        sample_rate, bus_width)

    # If we've been asked to dump SGPIO debug info, do so.
    if args.debug_sgpio:
        device.apis.logic_analyzer.dump_sgpio_configuration(False)
        print(device.read_debug_ring(), file=sys.stderr)
        sys.stderr.flush()

    # Print what we're doing and our status.
    log_function("Sampling {} channels at {}Hz.".format(
        bus_width, eng_notation(sample_rate)))
    log_function("Press Ctrl+C to stop reading data from device.")

    # If we have a target binary file, open the target filename and use that to store samples.
    if args.binary:
        bin_file = open(args.binary, 'wb')
        bin_file_name = args.binary

    # Otherwise, create an temporary file and use that. (It's automatically destroyed on close, which is fancy.)
    elif args.pulseview:
        try:
            holding_dir = os.path.dirname(os.path.abspath(args.pulseview))
        except:
            holding_dir = None

        bin_file = tempfile.NamedTemporaryFile(dir=holding_dir)
        bin_file_name = bin_file.name

    # Create queues of transfer objects that we'll use as a producer/consumer interface for our comm thread.
    empty_buffers = []
    full_buffers = []

    # Allocate a set of transfer buffers, so we don't have to continuously allocate them.
    for _ in range(DEFAULT_PREALLOCATED_BUFFERS):
        empty_buffers.append(allocate_transfer_buffer(buffer_size))

    # Finally, spawn the thread that will handle our data processing and output.
    termination_request = threading.Event()
    thread_arguments = (termination_request, args, bus_width, bin_file,
                        empty_buffers, full_buffers)
    data_thread = threading.Thread(target=background_process_data,
                                   args=thread_arguments)

    # Now that we're done with all of that setup, perform our actual sampling, in a tight loop,
    data_thread.start()
    device.apis.logic_analyzer.start()
    start_time = time.time()

    try:
        while True:

            # Grab a transfer buffer from the empty list...
            try:
                transfer_buffer = empty_buffers.pop()
            except IndexError:
                # If we don't have a buffer to fill, allocate a new one. It'll wind up in our buffer pool shortly.
                transfer_buffer = allocate_transfer_buffer(buffer_size)

            # Capture data from the device, and unpack it.
            device.comms.device.read(endpoint, transfer_buffer, 3000)

            # ... and pop it into the to-be-processed queue.
            full_buffers.append(transfer_buffer)

    except KeyboardInterrupt:
        pass
    except usb.core.USBError as e:
        log_error("")
        if e.errno == 32:
            log_error(
                "ERROR: Couldn't pull data from the device fast enough! Aborting."
            )
            log_error(
                "(Lowering the sample rate may help. Sometimes, switching to another USB bus / port may help.)"
            )
        else:
            log_error(
                "ERROR: Communications failure -- check the connection to -- and state of  -- the GreatFET. "
            )
            log_error(
                "(More debug information may be available if you run 'gf dmesg')."
            )
            log_error(e)
    finally:
        elapsed_time = time.time() - start_time

        # No matter what, once we're done stop the device from sampling.
        device.apis.logic_analyzer.stop()

        # Signal to our data processing thread that it's time to terminate.
        termination_request.set()

    # Wait for our data processing thread to complete.
    log_function('')
    log_function(
        'Capture terminated -- waiting for data processing to complete.')
    data_thread.join()

    # Flush whatever data we've read to disk, so it can be correctly read by subsequent operations.
    if args.pulseview:
        bin_file.flush()

    # Finally, generate our output.
    if args.binary:
        log_function("Binary data written to file '{}'.".format(args.binary))
    if args.pulseview:
        emit_sigrok_file(args.pulseview, bin_file_name, bus_width, sample_rate,
                         args.first_pin, channel_names)
        log_function(
            "Sigrok/PulseView compatible session file created: '{}'.".format(
                args.pulseview))

    # Print how long we sampled for, as a nicety.
    log_function("Sampled for {} seconds.".format(round(elapsed_time, 4)))
Esempio n. 19
0
def main():

    # Start off with a default packet state.
    current_packet_type = None
    current_packet_data = array.array('B')
    current_packet_remaining = 0
    next_data_packet_emit_after = []
    next_data_packet_timestamp = None

    current_usb_data = array.array('B')

    def emit_usb_packet(packet_data):
        """
        Emits a raw USB packet to the target format.
        """

        print("Packet: [{}]".format(packet_data))

    def is_valid_pid_byte(byte):
        """ Returns true iff the given byte could be a valid PID. """

        pid = byte & 0xf
        inverse = byte >> 4

        return (pid ^ inverse) == 0xf

    def hack_smoothe_out_jitter(packet_data, emit_point):
        """ XXX Horrorhack intended to "smoothe" over a missing firmware piece until it's implemented. """

        try:
            print("next PID would be {} -- valid: {}".format(
                packet_data[emit_point],
                is_valid_pid_byte(packet_data[emit_point])))

            # If the next byte is a valid PID, there's no need to hack anything.
            # Move along..
            if is_valid_pid_byte(packet_data[emit_point]):
                return 0
        except IndexError:
            pass

        print("trying to smoothe out a bit of jitter:")

        try:

            print("trying delta -1 [{}] -- {}".format(
                packet_data[emit_point - 1],
                is_valid_pid_byte(packet_data[emit_point - 1])))

            # Otherwise, if the previous byte was a valid PID, move back to it.
            if is_valid_pid_byte(packet_data[emit_point - 1]):
                return -1
        except IndexError:
            pass

        try:

            print("trying delta +1 [{}] -- {}".format(
                packet_data[emit_point + 1],
                is_valid_pid_byte(packet_data[emit_point + 1])))

            # Otherwise, if the previous byte was a valid PID, move back to it.
            if is_valid_pid_byte(packet_data[emit_point + 1]):
                return 1
        except IndexError:
            pass

        return 0

    def handle_capture_packet(packet_type, packet_data):
        """
        Handles a received full packet from the analyzer.
        """

        nonlocal current_packet_type, current_packet_data, current_packet_remaining
        nonlocal next_data_packet_emit_after, next_data_packet_timestamp, current_usb_data

        # If this is an "end event" packet, grab the point at which
        # we're supposed to emit the packet.
        if packet_type == PACKET_TYPE_EVENT_END_OK:

            if (not next_data_packet_emit_after) or (
                    next_data_packet_emit_after[-1] != packet_data[0]):
                next_data_packet_emit_after.append(packet_data[0])

        # If this is start packet, grab the timestamp from it.
        elif packet_type == PACKET_TYPE_EVENT_START:

            # FIXME: implement
            pass

        # If this is a USB data packet, handle it.
        elif packet_type == PACKET_TYPE_USB_DATA:
            print("got {} bytes of USB data [{}] <emit at: {}>".format(
                len(packet_data), packet_data, next_data_packet_emit_after))

            existing_packet_length = len(current_usb_data)

            # Add the data to the current packet.
            current_usb_data.extend(packet_data)

            # Store that we're handled 0 bytes into the current packet.
            position_in_packet = 0

            # If this packet ends a USB packet, emit the completed usb packet.
            while next_data_packet_emit_after:

                emit_after_bytes = next_data_packet_emit_after.pop(
                    0) - position_in_packet + 1

                print("emit after: {} bytes [data: {}]".format(
                    emit_after_bytes, current_usb_data))

                # Emit the USB packet up until this point...
                emit_point = existing_packet_length + emit_after_bytes

                # XXX: Temporary hack to smoothe out single-byte event offsets until
                # the firmware properly has NXT and DIR tied to an SCT counter.
                delta = hack_smoothe_out_jitter(current_usb_data, emit_point)
                emit_point += delta
                emit_after_bytes += delta

                emit_usb_packet(current_usb_data[0:emit_point])

                # ... and mark ourselves as already having emitted the relevant bytes, so
                # the future "emit afters" can be scaled properly.
                position_in_packet += emit_after_bytes

                # ... and remove those packets from the buffer.
                del current_usb_data[0:emit_point]

            next_data_packet_emit_after = []

    def parse_capture_packets(samples, args, bin_file):
        """
        Parses a set of packets coming from a USB capture device.
        """

        nonlocal current_packet_type, current_packet_data, current_packet_remaining
        nonlocal next_data_packet_emit_after, next_data_packet_timestamp, current_usb_data

        # Parse all of the samples we have in our buffer.
        while samples:

            sample = samples.pop(0)
            #print("sample: {} / current_packet_remaining: {}".format(sample, current_packet_remaining))

            # If we have data remaining in our packet, parse this sample as data.
            if current_packet_remaining:
                current_packet_data.append(sample)
                current_packet_remaining -= 1

                # If we just completed a given packet, handle it.
                if current_packet_remaining == 0:
                    handle_capture_packet(current_packet_type,
                                          current_packet_data)

                    # Clear out our packet state.
                    del current_packet_data[:]

            # Otherwise, handle this as a new packet.
            elif (current_packet_remaining == 0) and (sample in PACKET_SIZES):
                current_packet_type = sample
                current_packet_remaining = PACKET_SIZES[current_packet_type] - 1
            else:
                raise IOError(
                    "unknown packet type {}! stream error?\n".format(sample))

    # Set up our argument parser.
    parser = GreatFETArgumentParser(
        description="Simple Rhododendron capture utility for GreatFET.",
        verbose_by_default=True)
    parser.add_argument(
        '-o',
        '-b',
        '--binary',
        dest='binary',
        metavar='<filename>',
        type=str,
        help="Write the raw samples captured to a file with the provided name."
    )
    parser.add_argument(
        '--m0',
        dest="m0",
        type=argparse.FileType('rb'),
        metavar='<filename>',
        help=
        "loads the specific m0 coprocessor 'loadable' instead of the default Rhododendron one"
    )
    parser.add_argument('-F',
                        '--full-speed',
                        dest='speed',
                        action='store_const',
                        const=SPEED_FULL,
                        default=SPEED_HIGH,
                        help="Capture full-speed data.")
    parser.add_argument('-L',
                        '--low-speed',
                        dest='speed',
                        action='store_const',
                        const=SPEED_LOW,
                        default=SPEED_HIGH,
                        help="Capture low-speed data.")
    parser.add_argument('-H',
                        '--high-speed',
                        dest='speed',
                        action='store_const',
                        const=SPEED_HIGH,
                        help="Capture high-speed data. The default.")
    parser.add_argument(
        '-O',
        '--stdout',
        dest='write_to_stdout',
        action='store_true',
        help=
        'Provide this option to log the received data to the stdout.. Implies -q.'
    )

    # And grab our GreatFET.
    args = parser.parse_args()

    # If we're writing binary samples directly to stdout, don't emit logs to stdout; otherwise, honor the
    # --quiet flag.
    if args.write_to_stdout:
        log_function = log_silent
    else:
        log_function = parser.get_log_function()

    # Ensure we have at least one write operation.
    if not (args.binary or args.write_to_stdout):
        parser.print_help()
        sys.exit(-1)

    # Find our GreatFET.
    device = parser.find_specified_device()

    # Bring our Rhododendron board online; and capture communication parameters.
    buffer_size, endpoint = device.apis.usb_analyzer.initialize(
        args.speed, timeout=10000, comms_timeout=10000)

    # $Load the Rhododendron firmware loadable into memory...
    try:
        if args.m0:
            data = args.m0.read()
        else:
            data = read_rhododendron_m0_loadable()
    except (OSError, TypeError):
        log_error("Can't find a Rhododendron m0 program to load!")
        log_error("We can't run without one.")
        sys.exit(-1)

    # Debug only: setup a pin to track when we're handling SGPIO data.
    debug_pin = device.gpio.get_pin('J1_P3')
    debug_pin.set_direction(debug_pin.DIRECTION_OUT)

    # ... and then run it on our m0 coprocessor.
    device.m0.run_loadable(data)

    # Print what we're doing and our status.
    log_function("Reading raw {}-speed USB data!\n".format(
        SPEED_NAMES[args.speed]))
    log_function("Press Ctrl+C to stop reading data from device.")

    # If we have a target binary file, open the target filename and use that to store samples.
    bin_file = None
    if args.binary:
        bin_file = open(args.binary, 'wb')
        bin_file_name = args.binary

    # Now that we're done with all of that setup, perform our actual sampling, in a tight loop,
    device.apis.usb_analyzer.start_capture()

    transfer_buffer = allocate_transfer_buffer(buffer_size)

    total_captured = 0

    try:
        while True:

            # Capture data from the device, and unpack it.
            try:
                new_samples = device.comms.device.read(
                    endpoint, transfer_buffer, SAMPLE_DELIVERY_TIMEOUT_MS)
                samples = transfer_buffer[0:new_samples - 1]

                total_captured += new_samples
                log_function("Captured {} bytes.".format(total_captured),
                             end="\r")

                parse_capture_packets(samples, args, bin_file)

            except usb.core.USBError as e:
                if e.errno != errno.ETIMEDOUT:
                    raise

    except KeyboardInterrupt:
        pass
    except usb.core.USBError as e:
        log_error("")
        if e.errno == 32:
            log_error(
                "ERROR: Couldn't pull data from the device fast enough! Aborting."
            )
        else:
            log_error(
                "ERROR: Communications failure -- check the connection to -- and state of  -- the GreatFET. "
            )
            log_error(
                "(More debug information may be available if you run 'gf dmesg')."
            )
            log_error(e)
    finally:

        # No matter what, once we're done stop the device from sampling.
        device.apis.usb_analyzer.stop_capture()

        if args.binary:
            log_function("Binary data written to file '{}'.".format(
                args.binary))
Esempio n. 20
0
def main():

    commands = {
        'info': print_flash_info,
        'erase': erase_chip,
        'read': dump_chip,
        'write': program_chip
    }

    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        description="Utility for programming and dumping SPI flash chips",
        verbose_by_default=True)
    parser.add_argument('command',
                        choices=commands,
                        help='the operation to complete')
    parser.add_argument(
        'filename',
        metavar="[filename]",
        nargs='?',
        help='the filename to read or write to, for read/write operations')

    parser.add_argument('-a',
                        '--address',
                        metavar='<addr>',
                        default=0,
                        type=from_eng_notation,
                        help="Starting offset in the given flash memory.")
    parser.add_argument('-l',
                        '--length',
                        metavar='<length>',
                        type=from_eng_notation,
                        default=None,
                        help="number of bytes to read (default: flash size)")
    parser.add_argument(
        '-E',
        '--no-autoerase',
        action='store_false',
        dest='autoerase',
        help=
        "If provided, the target flash will not be erased before a write operation."
    )
    parser.add_argument(
        '-C',
        '--cautious',
        '--cowardly',
        action='store_true',
        dest='cowardly',
        help="Refuses to do anything that might overwrite or lose chip data.")
    parser.add_argument(
        '-S',
        '--no-spdf',
        dest='autodetect',
        action='store_false',
        help="Don't attempt to use SPDF to autodetect flash parameters.")
    parser.add_argument(
        '-R',
        '--require-spdf',
        dest='allow_fallback',
        action='store_false',
        help="Only use SPDF; ignore any argument provided as fall-back options."
    )
    parser.add_argument(
        '-T',
        '--auto-truncate',
        dest='truncate',
        action='store_true',
        help=
        "If provided, any read operations will truncate trailing unprogrammed words (0xFFs)."
    )
    parser.add_argument(
        '--page-size',
        metavar='<bytes>',
        type=int,
        default=256,
        help=
        "manually specify the page size of the target flash; for use with -S")
    parser.add_argument(
        '--flash-size',
        metavar='<bytes>',
        type=int,
        default=8192,
        help=
        "manually specify the capacity of the target flash; for use with -S")
    parser.add_argument(
        '-J',
        '--allow-null-jedec-id',
        dest='bypass_jedec',
        action='store_true',
        help=
        "Allow the device to work even if it doesn't appear to support a JEDEC ID."
    )

    args = parser.parse_args()
    device = parser.find_specified_device()

    if args.command == 'info':
        args.verbose = True
    elif args.filename == "-":
        args.verbose = False

    # Grab our log functions.
    log_function, log_error = parser.get_log_functions()

    try:
        # TODO: use a GreatFET method to automatically instantiate spi_flash

        # Figure out the "override" page and flash size for any arguments provided.
        # If autodetection is enabled and works, these aren't used.
        maximum_address = args.flash_size - 1
        num_pages = (args.flash_size + (args.page_size - 1)) // args.page_size

        # Create a SPI flash object.
        spi_flash = device.create_programmer(
            'spi_flash',
            args.autodetect,
            args.allow_fallback,
            page_size=args.page_size,
            maximum_address=maximum_address,
            allow_null_jedec=args.bypass_jedec)

        # If we have a device that's ancient enough to not speak JEDEC, notify the user. Pithily.
        if args.bypass_jedec and spi_flash.manufacturer_id == 0xff and spi_flash.part_id == 0xffff:
            log_function(
                "I... really can't see an SPI flash here. I'll trust you.")

    except CommandFailureError:
        log_error("This device doesn't appear to support identifying itself.")
        log_error(" You'll need to specify the page and flash size manually.")
        sys.exit(-1)
    except IOError as e:
        log_error(str(e))
        log_error("If you believe you have a device properly connected, you")
        log_error(" may want to try again with --allow-null-jedec-id.")
        log_error("")
        sys.exit(-2)

    command = commands[args.command]
    command(spi_flash, log_function, log_error, args)
Esempio n. 21
0
def main():
    # Set up a simple argument parser.
    parser = GreatFETArgumentParser(
        dfu=True,
        description="Utility for flashing firmware on GreatFET boards")
    parser.add_argument('-a',
                        '--address',
                        metavar='<n>',
                        type=int,
                        help="starting address (default: 0)",
                        default=0)
    parser.add_argument(
        '-l',
        '--length',
        metavar='<n>',
        type=int,
        help="number of bytes to read (default: {})".format(MAX_FLASH_LENGTH),
        default=MAX_FLASH_LENGTH)
    parser.add_argument('-r',
                        '--read',
                        dest='read',
                        metavar='<filename>',
                        type=str,
                        help="Read data into file",
                        default='')
    parser.add_argument('-w',
                        '--write',
                        dest='write',
                        metavar='<filename>',
                        type=str,
                        help="Write data from file",
                        default='')
    parser.add_argument(
        '-R',
        '--reset',
        dest='reset',
        action='store_true',
        help="Reset GreatFET after performing other operations.")
    args = parser.parse_args()

    # Validate our options.

    # If we don't have an option, print our usage.
    if not any((
            args.read,
            args.write,
            args.reset,
    )):
        parser.print_help()
        sys.exit(0)

    # Determine whether we're going to log to the stdout, or not at all.
    log_function = parser.get_log_function()

    # If we're supposed to install firmware via a DFU stub, install it first.
    if args.dfu:
        try:
            load_dfu_stub(args)
        except DeviceNotFoundError:
            print("Couldn't find a GreatFET-compatible board in DFU mode!",
                  file=sys.stderr)
            sys.exit(errno.ENODEV)

    # Create our GreatFET connection.
    log_function("Trying to find a GreatFET device...")
    device = parser.find_specified_device()
    log_function("{} found. (Serial number: {})".format(
        device.board_name(), device.serial_number()))

    # Ensure that the device supports an onboard SPI flash.
    try:
        device.onboard_flash
    except AttributeError:
        print(
            "The attached GreatFET ({}) doesn't appear to have an SPI flash to program!"
            .format(device.board_name()),
            file=sys.stderr)
        sys.exit(errno.ENOSYS)

    # If we have a write command, write first, to match the behavior of hackrf_spiflash.
    if args.write:
        log_function("Writing data to SPI flash...")
        spi_flash_write(device, args.write, args.address, log_function)
        log_function("Write complete!")
        if not (args.reset or args.dfu):
            log_function(
                "Reset not specified; new firmware will not start until next reset."
            )

    # Handle any read commands.
    if args.read:
        log_function("Reading data from SPI flash...")
        spi_flash_read(device, args.read, args.address, args.length,
                       log_function)
        log_function("Read complete!")

    # Finally, reset the target
    if args.reset or args.dfu:
        log_function("Resetting GreatFET...")
        device.reset(reconnect=False)
        log_function("Reset complete!")
Esempio n. 22
0
def main():

    # Simple type-arguments for parsing.
    int_from_msps = lambda x: from_eng_notation(
        x, units=['Hz', 'SPS'], to_type=int)
    int_from_eng = lambda x: from_eng_notation(x, to_type=int)

    # Set up our argument parser.
    parser = GreatFETArgumentParser(
        description="Logic analyzer implementation for GreatFET",
        verbose_by_default=True)
    parser.add_argument(
        'command',
        choices=commands,
        help='the pattern shape to generate, or command to execute')
    parser.add_argument(
        '-n',
        '--samples',
        metavar='samples',
        type=int_from_eng,
        default=64,
        dest='samples',
        help='the number of samples to generate of the given pattern, up to 32K'
    )
    parser.add_argument(
        '-w',
        '--width',
        metavar='bus_width',
        type=int,
        default=8,
        dest='bus_width',
        help='the width of the bus, in bits; up to 16 [default: 8]')
    parser.add_argument(
        '-f',
        '--samplerate',
        metavar='samples_per_second',
        type=int_from_msps,
        default=1000000,
        dest='sample_rate',
        help='samples to emit per second; up to 204MSPS [default: 1MSPS]')
    parser.add_argument(
        '--oneshot',
        dest='repeat',
        action='store_false',
        help='If provided, the given pattern will be shifted out only once.')
    parser.add_argument(
        '--debug-sgpio',
        dest='debug_sgpio',
        action='store_true',
        help=
        'Developer option for debugging; dumps the SGPIO configuration after starting.'
    )

    args = parser.parse_args()
    log_function, log_error = parser.get_log_functions()

    # Find our GreatFET.
    device = parser.find_specified_device()

    # Ensure the GreatFET supports our API.
    if not device.supports_api('pattern_generator'):
        log_error(
            "The connected GreatFET doesn't seem to support pattern generation. A firmware upgrade may help."
        )

    # Create our pattern generator object.
    pattern_generator = PatternGenerator(device,
                                         sample_rate=args.sample_rate,
                                         bus_width=args.bus_width)

    # TODO: truncate things to the pattern generator object's limits

    # Figure out how the user wants their samples generated (or what the want done, in general).
    command_to_execute = commands[args.command]

    # Execute the core command, which usually generates samples.
    samples = command_to_execute(args, pattern_generator)

    # If the generator has generated samples, scan them out.
    if samples:
        pattern_generator.scan_out_pattern(samples, args.repeat)

    # If we've been asked to dump our SGPIO configuration, do so.
    if args.debug_sgpio:
        log_error(pattern_generator.dump_sgpio_config())

    # Log what we've done to the user.
    if samples:
        log_function("Scanning out {} samples at {}.".format(
            len(samples), eng_notation(args.sample_rate, unit='SPS')))
        log_function("Run '{} stop' to halt generation.".format(sys.argv[0]))
        log_function("")