From: Bert Vermeulen Date: Tue, 23 Oct 2012 21:36:32 +0000 (+0200) Subject: add new-driver tool X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=a65d66c08881d65a6d6e37be8efc1a24e4f60592;p=sigrok-util.git add new-driver tool --- diff --git a/source/README.new-driver b/source/README.new-driver new file mode 100644 index 0000000..1ab32fb --- /dev/null +++ b/source/README.new-driver @@ -0,0 +1,27 @@ +The 'new-driver' script is meant to reduce the "red tape" involved in creating +a new hardware driver for sigrok. It checks out a new copy of libsigrok from +the git repository, makes entries in the build system for your device, and +creates source code stubs to help you get started. + +If you're writing a driver for a new multimeter (DMM) called the +'Flupe Goodluck', invoke the script like this: + + ./new-driver Flupe Goodluck + +The output will show something like this: + +Cloning into 'libsigrok'... +[master d5f915f] flupe-goodluck: initial driver skeleton + 6 files changed, 321 insertions(+) + create mode 100644 hardware/flupe-goodluck/Makefile.am + create mode 100644 hardware/flupe-goodluck/api.c + create mode 100644 hardware/flupe-goodluck/protocol.c + create mode 100644 hardware/flupe-goodluck/protocol.h +0001-flupe-goodluck-initial-driver-skeleton.patch + +The last line shows the name of the patch file that's created for you. It's +a proper git mail patch, and can be applied to your tree like this: + +cd /some/path/libsigrok +git am /some/place/0001-flupe-goodluck-initial-driver-skeleton.patch + diff --git a/source/drv-Makefile.am b/source/drv-Makefile.am new file mode 100644 index 0000000..7af869e --- /dev/null +++ b/source/drv-Makefile.am @@ -0,0 +1,33 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) ${year} ${author} <${email}> +## +## 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 +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program 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 General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +## + +if HW_${upper} + +# Local lib, this is NOT meant to be installed! +noinst_LTLIBRARIES = libsigrokhw${lib}.la + +libsigrokhw${lib}_la_SOURCES = \ + api.c \ + protocol.c \ + protocol.h + +libsigrokhw${lib}_la_CFLAGS = \ + -I$(top_srcdir) + +endif diff --git a/source/drv-api.c b/source/drv-api.c new file mode 100644 index 0000000..e9b046c --- /dev/null +++ b/source/drv-api.c @@ -0,0 +1,194 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) ${year} ${author} <${email}> + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "config.h" +#include "protocol.h" + + +SR_PRIV struct sr_dev_driver driver_info; +static struct sr_dev_driver *di = &${lib}_driver_info; + + +/* Properly close and free all devices. */ +static int clear_instances(void) +{ + struct sr_dev_inst *sdi; + struct drv_context *drvc; + struct dev_context *devc; + GSList *l; + + drvc = di->priv; + for (l = drvc->instances; l; l = l->next) { + if (!(sdi = l->data)) + continue; + if (!(devc = sdi->priv)) + continue; + + /* TODO */ + + sr_dev_inst_free(sdi); + } + + g_slist_free(drvc->instances); + drvc->instances = NULL; + + return SR_OK; +} + +static int hw_init(void) +{ + struct drv_context *drvc; + + if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) { + sr_err("${short}: driver context malloc failed."); + return SR_ERR; + } + + /* TODO */ + + di->priv = drvc; + + return SR_OK; +} + +static GSList *hw_scan(GSList *options) +{ + struct drv_context *drvc; + GSList *devices; + + (void)options; + devices = NULL; + drvc = di->priv; + drvc->instances = NULL; + + /* TODO */ + + return devices; +} + +static GSList *hw_dev_list(void) +{ + struct drv_context *drvc; + + drvc = di->priv; + + return drvc->instances; +} + +static int hw_dev_open(struct sr_dev_inst *sdi) +{ + + /* TODO */ + + return SR_OK; +} + +static int hw_dev_close(struct sr_dev_inst *sdi) +{ + + /* TODO */ + + return SR_OK; +} + +static int hw_cleanup(void) +{ + + clear_instances(); + + /* TODO */ + + return SR_OK; +} + +static int hw_info_get(int info_id, const void **data, + const struct sr_dev_inst *sdi) +{ + + + switch (info_id) { + /* TODO */ + default: + return SR_ERR_ARG; + } + + return SR_OK; +} + +static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap, + const void *value) +{ + int ret; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + ret = SR_OK; + switch (hwcap) { + /* TODO */ + default: + ret = SR_ERR_ARG; + } + + return ret; +} + +static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, + void *cb_data) +{ + + /* TODO */ + + return SR_OK; +} + +static int hw_dev_acquisition_stop(const struct sr_dev_inst *sdi, + void *cb_data) +{ + + (void)cb_data; + + if (sdi->status != SR_ST_ACTIVE) + return SR_ERR; + + /* TODO */ + + return SR_OK; +} + +SR_PRIV struct sr_dev_driver ${lib}_driver_info = { + .name = "${short}", + .longname = "${name}", + .api_version = 1, + .init = hw_init, + .cleanup = hw_cleanup, + .scan = hw_scan, + .dev_list = hw_dev_list, + .dev_clear = clear_instances, + .dev_open = hw_dev_open, + .dev_close = hw_dev_close, + .info_get = hw_info_get, + .dev_config_set = hw_dev_config_set, + .dev_acquisition_start = hw_dev_acquisition_start, + .dev_acquisition_stop = hw_dev_acquisition_stop, + .priv = NULL, +}; diff --git a/source/drv-protocol.c b/source/drv-protocol.c new file mode 100644 index 0000000..f894744 --- /dev/null +++ b/source/drv-protocol.c @@ -0,0 +1,44 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) ${year} ${author} <${email}> + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" +#include "config.h" +#include "protocol.h" +#include + + +SR_PRIV int ${lib}_receive_data(int fd, int revents, void *cb_data) +{ + const struct sr_dev_inst *sdi; + struct dev_context *devc; + + if (!(sdi = cb_data)) + return TRUE; + + if (!(devc = sdi->priv)) + return TRUE; + + if (revents == G_IO_IN) { + /* TODO */ + } + + return TRUE; +} diff --git a/source/drv-protocol.h b/source/drv-protocol.h new file mode 100644 index 0000000..c12e6ba --- /dev/null +++ b/source/drv-protocol.h @@ -0,0 +1,38 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) ${year} ${author} <${email}> + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef LIBSIGROK_${upper}_H +#define LIBSIGROK_${upper}_H + +/* Private, per-device-instance driver context. */ +struct dev_context { + uint64_t limit_samples; + uint64_t limit_msec; + + /* Opaque pointer passed in by the frontend. */ + void *cb_data; + + /* Runtime. */ + uint64_t num_samples; +}; + +SR_PRIV int ${lib}_receive_data(int fd, int revents, void *cb_data); + +#endif diff --git a/source/new-driver b/source/new-driver new file mode 100755 index 0000000..78d4cb6 --- /dev/null +++ b/source/new-driver @@ -0,0 +1,265 @@ +#!/usr/bin/python3 +# +# This file is part of the sigrok project. +# +# Copyright (C) 2012 Bert Vermeulen +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import sys +import tempfile +from subprocess import Popen, PIPE +import shutil +import re +import socket +import datetime + +TMPL_AUTOCONF_AC_ARG_ENABLE = """AC_ARG_ENABLE(${short}, AC_HELP_STRING([--enable-${short}], + [enable ${name} driver support [default=yes]]), + [HW_${upper}="$enableval"], + [HW_${upper}=yes]) +AM_CONDITIONAL(HW_${upper}, test x$HW_${upper} = xyes) +if test "x$HW_${upper}" = "xyes"; then + AC_DEFINE(HAVE_HW_${upper}, 1, [${name} driver support]) +fi + +""" +TMPL_AUTOCONF_AC_CONFIG_FILES = "\t\t hardware/${short}/Makefile\n" +TMPL_AUTOCONF_SUMMARY = 'echo " - ${summary}"\n' +TMPL_HWMAKE_SUBDIR = '\t${short}' +FILE_DRV_MAKEFILE = 'drv-Makefile.am' +FILE_DRV_API = 'drv-api.c' +FILE_DRV_PROTOCOL = 'drv-protocol.c' +FILE_DRV_PROTOCOL_H = 'drv-protocol.h' + +def tmpl(template): + out = re.sub(r'\${([^}]+)}', lambda x: str(names[x.group(1)]), template) + + return out + + +def tmpl_file(filename): + template = open(TMPLDIR + '/' + filename).read() + + return tmpl(template) + + +def new_driver(): + tmp = tempfile.mkdtemp() + try: + os.chdir(tmp) + out, err = Popen("git clone " + LIBSR, shell=True, stderr=PIPE).communicate() + if err: + raise Exception(err.decode()) + gitdir = tmp + '/libsigrok/' + do_configure_ac(gitdir) + do_hwmake(gitdir) + do_driverskel(gitdir) + make_patch(gitdir) + except Exception as e: + print(e) + shutil.rmtree(tmp) + + +def do_configure_ac(gitdir): + cacpath = gitdir + 'configure.ac' + configure_ac = open(cacpath).read() + + # add AC_ARG_ENABLE option + out = '' + state = 'copy' + for line in configure_ac.split('\n')[:-1]: + if state == 'copy': + if line == "# Hardware support '--enable' options.": + state = 'acarg' + elif state == 'acarg': + m = re.match('AC_ARG_ENABLE\(([^,]+)', line) + if m: + drv_short = m.group(1) + if drv_short.lower() > names['short']: + out += tmpl(TMPL_AUTOCONF_AC_ARG_ENABLE) + state = 'done' + if line == '# Checks for libraries.': + # new one at the end + out += tmpl(TMPL_AUTOCONF_AC_ARG_ENABLE) + state = 'done' + out += line + '\n' + if state != 'done': + raise Exception('AC_ARG_ENABLE markers not found in configure.ac') + configure_ac = out + + # add driver Makefile to AC_CONFIG_FILES + out = '' + state = 'copy' + for line in configure_ac.split('\n')[:-1]: + if state == 'copy': + if line.find("AC_CONFIG_FILES([Makefile") > -1: + state = 'acconf' + elif state == 'acconf': + m = re.match('\t\t hardware/([^/]+)/Makefile', line) + if m: + drv_short = m.group(1) + if drv_short.lower() > names['short']: + out += tmpl(TMPL_AUTOCONF_AC_CONFIG_FILES) + state = 'done' + else: + # new one at the end + out += tmpl(TMPL_AUTOCONF_AC_CONFIG_FILES) + state = 'done' + out += line + '\n' + if state != 'done': + raise Exception('AC_CONFIG_FILES marker not found in configure.ac') + configure_ac = out + + # add summary line + out = '' + state = 'copy' + names['summary'] = "%s%s $HW_%s" % (names['name'], + '.' * (32 - len(names['name'])), names['upper']) + for line in configure_ac.split('\n')[:-1]: + if state == 'copy': + if line.find('Enabled hardware drivers') > -1: + state = 'echo' + elif state == 'echo': + m = re.match('echo " - ([^\.]+)', line) + if m: + drv_short = m.group(1) + if drv_short.lower() > names['name'].lower(): + out += tmpl(TMPL_AUTOCONF_SUMMARY) + state = 'done' + else: + # new one at the end + out += tmpl(TMPL_AUTOCONF_SUMMARY) + state = 'done' + out += line + '\n' + if state != 'done': + raise Exception('summary marker not found in configure.ac') + configure_ac = out + + open(cacpath, 'w').write(configure_ac) + + +def do_hwmake(gitdir): + path = gitdir + 'hardware/Makefile.am' + hwmake = open(path).read() + + # add AC_ARG_ENABLE option + out = '' + state = 'copy' + for line in hwmake.split('\n')[:-1]: + if state == 'copy': + if line.find('SUBDIRS =') > -1: + state = 'subdirs' + elif state == 'subdirs': + m = re.match('\t([^ \\\]+\s*\\\)', line) + if m: + drv_short = m.group(1) + if drv_short.lower() > names['short']: + out += tmpl(TMPL_HWMAKE_SUBDIR) + ' \\\n' + state = 'done' + else: + out += tmpl(TMPL_HWMAKE_SUBDIR) + ' \\\n' + state = 'done' + out += line + '\n' + if state != 'done': + raise Exception('SUBDIRS markers not found in hardware/Makefile.am') + hwmake = out + + open(path, 'w').write(hwmake) + + +def do_driverskel(gitdir): + drvdir = gitdir + 'hardware/' + names['short'] + os.mkdir(drvdir) + open(drvdir + '/Makefile.am', 'w').write(tmpl_file(FILE_DRV_MAKEFILE)) + open(drvdir + '/api.c', 'w').write(tmpl_file(FILE_DRV_API)) + open(drvdir + '/protocol.c', 'w').write(tmpl_file(FILE_DRV_PROTOCOL)) + open(drvdir + '/protocol.h', 'w').write(tmpl_file(FILE_DRV_PROTOCOL_H)) + + +def make_patch(gitdir): + os.chdir(gitdir) + command('git add hardware/' + names['short']) + cmd = 'git commit -m "%s: initial driver skeleton" ' % names['short'] + cmd += 'configure.ac hardware/Makefile.am hardware/' + names['short'] + command(cmd) + cmd = "git format-patch HEAD~1" + out, err = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate() + if err: + raise Exception(err.decode()) + patch = out.decode().strip() + shutil.move(gitdir + '/' + patch, scriptdir + '/' + patch) + print(patch) + + +def command(cmd): + out, err = Popen(cmd, shell=True, stderr=PIPE).communicate() + if err: + raise Exception(err.decode()) + + +def parse_gitconfig(): + try: + author = email = None + for line in open(os.environ['HOME'] + '/.gitconfig').readlines(): + m = re.match('\s*(\w+)\s*=\s*(.*)\s*$', line) + if m: + key, value = m.groups() + if key == 'name': + author = value + elif key == 'email': + email = value + if author and email: + break + except: + pass + if not author or not email: + print("Please put your name and email in ~/.gitconfig") + sys.exit() + + return author, email + +# +# main +# + +scriptdir = os.getcwd() +if socket.gethostname() == 'sigrok': + LIBSR = '/data/git/libsigrok' + TMPLDIR = '/data/tools/tmpl' +else: + LIBSR = 'git://sigrok.org/libsigrok' + TMPLDIR = scriptdir + +if len(sys.argv) < 2: + print("Usage: new-driver.py ") + sys.exit() + +author, email = parse_gitconfig() +name = ' '.join(sys.argv[1:]) +names = { + 'name': name, + 'short': re.sub('[^a-z0-9]', '-', name.lower()), + 'lib': re.sub('[^a-z0-9]', '_', name.lower()), + 'upper': re.sub('[^A-Z0-9]', '_', name.upper()), + 'libupper': re.sub('[^A-Z0-9]', '', name.upper()), + 'year': datetime.datetime.now().year, + 'author': author, + 'email': email, +} +new_driver() + +