Browse files

libusense: Initial import of library and command line utility

Using
-----

 $ usense
 Usage:

 $ usense
 usb:3.1
 usb:3.2
 usb:4.1
 $ usense usb:3.2
 device=usb:3.2
 reading=27.4
 vendor=Vernier
 device=Go!Temp
 calibration.mult=1.002
 calibration.add=-0.05
 $ usense 3.2 reading
 27.4

Configuration
-------------

 $ usense usb:3.2
 device=usb:3.2
 reading=27.4
 vendor=Vernier
 device=Go!Temp
 calibration.mult=1.002
 calibration.add=-0.05
 $ usense usb:3.2 calibration.mult 1.003
 $ usense usb:3.2 calibration.mult
 calibration.mult=1.003


Signed-off-by: Jason S. McMullan <jason.mcmullan@gmail.com>
  • Loading branch information...
1 parent 63d7b3a commit b867bb7a212cc09c76d0eaa7a8d704718c8cc821 @ezrec committed Jun 24, 2009
Showing with 908 additions and 220 deletions.
  1. +1 −0 Makefile.am
  2. +34 −0 README
  3. +2 −1 autoconf.sh
  4. +2 −0 configure.ac
  5. +9 −3 src/Makefile.am
  6. +89 −32 src/TEMPer.c
  7. +6 −48 src/ch341.c
  8. +1 −1 src/ch341.h
  9. +66 −135 src/gotemp.c
  10. +128 −0 src/main.c
  11. +28 −0 src/units.h
  12. +421 −0 src/usense.c
  13. +121 −0 src/usense.h
View
1 Makefile.am
@@ -1,2 +1,3 @@
+ACLOCAL_AMFLAGS=-I m4
SUBDIRS=src
View
34 README
@@ -22,3 +22,37 @@ support files. From there, just run:
$ ./configure
$ make
$ sudo make install
+
+Using
+-----
+
+ $ usense
+ Usage:
+
+ $ usense
+ usb:3.1
+ usb:3.2
+ usb:4.1
+ $ usense usb:3.2
+ device=usb:3.2
+ reading=27.4
+ vendor=Vernier
+ device=Go!Temp
+ calibration.mult=1.002
+ calibration.add=-0.05
+ $ usense 3.2 reading
+ 27.4
+
+Configuration
+-------------
+
+ $ usense usb:3.2
+ device=usb:3.2
+ reading=27.4
+ vendor=Vernier
+ device=Go!Temp
+ calibration.mult=1.002
+ calibration.add=-0.05
+ $ usense usb:3.2 calibration.mult 1.003
+ $ usense usb:3.2 calibration.mult
+ calibration.mult=1.003
View
3 autoconf.sh
@@ -6,7 +6,8 @@
# Re-generate the automake and autoconf files
# Useful when checking out from the git repo.
-aclocal
+libtoolize -i
+aclocal -I m4
autoheader
automake --add-missing --foreign
autoconf
View
2 configure.ac
@@ -5,9 +5,11 @@ AC_PREREQ([2.63])
AC_INIT([Aquaria], [0.2], [jason.mcmullan@gmail.com])
AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
# Checks for programs.
AC_PROG_CC
+AC_PROG_LIBTOOL
# Checks for libraries.
AC_CHECK_LIB([usb], [usb_init])
View
12 src/Makefile.am
@@ -1,10 +1,16 @@
+include_HEADERS = usense.h
-bin_PROGRAMS = gotemp TEMPer
+bin_PROGRAMS = usense
-gotemp_SOURCES = gotemp.c
+usense_SOURCES = main.c
+usense_LDADD = libusense.la
-TEMPer_SOURCES = \
+lib_LTLIBRARIES = libusense.la
+
+libusense_la_SOURCES = \
+ usense.h usense.c \
+ gotemp.c \
TEMPer.c \
ch341.c ch341.h \
i2c-algo-bit.c i2c-algo-bit.h i2c.h
View
121 src/TEMPer.c
@@ -21,11 +21,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
+
+#include "usense.h"
+#include "units.h"
#include "ch341.h"
#include "i2c.h"
#include "i2c-algo-bit.h"
+struct temper {
+ struct ch341 *ch;
+ struct i2c_adapter adap;
+ struct i2c_algo_bit_data i2c_bit;
+};
+
static void temper_setsda(void *data, int state)
{
struct ch341 *ch = data;
@@ -129,61 +139,108 @@ static int temp_read(struct i2c_adapter *adap, int reg, int16_t *val)
return err;
}
-int main(int argc, char **argv)
+static int TEMPer_update(struct usense_device *dev, void *priv)
+{
+ int16_t temp;
+ struct temper *temper = priv;
+ int err;
+
+ /* Dump temp */
+ err = temp_read(&temper->adap, REG_TEMP, &temp);
+ if (err < 0) {
+ fprintf(stderr, "%s: Can't read temperature\n", usense_device_name(dev));
+ return -EINVAL;
+ } else {
+ /* microKelvin */
+ char buff[48];
+ double celsius = temp / 256.0;
+ uint64_t mkelvin = (uint64_t)(C_TO_K(celsius) * 1000000);
+ snprintf(buff, sizeof(buff), "%llu", (unsigned long long)mkelvin);
+ usense_prop_set(dev, "reading", buff);
+ }
+
+ return 0;
+}
+
+static int TEMPer_attach(struct usense_device *dev, struct usb_dev_handle *usb, void **priv)
{
/* Connect to ch341 */
int err;
+ struct temper *temper;
struct ch341 *ch;
- struct i2c_adapter adap;
- struct i2c_algo_bit_data i2c_bit;
uint8_t cfg;
int16_t temp;
- ch = ch341_open(0);
+ ch = ch341_open(usb);
if (ch == NULL) {
- return EXIT_FAILURE;
+ return -ENODEV;
}
- i2c_bit.data = ch;
- i2c_bit.setsda = temper_setsda;
- i2c_bit.setscl = temper_setscl;
- i2c_bit.getsda = temper_getsda;
- i2c_bit.getscl = NULL;
- i2c_bit.udelay = 50; /* in ms */
- i2c_bit.timeout = 1000; /* in ms */
+ temper = calloc(1, sizeof(*temper));
+ temper->ch = ch;
- adap.timeout = 1000;
- adap.retries = 3;
- strncpy(&adap.name[0], "ch341-i2c", sizeof(adap.name));
- adap.algo_data = &i2c_bit;
- i2c_bit_add_bus(&adap);
+ temper->i2c_bit.data = ch;
+ temper->i2c_bit.setsda = temper_setsda;
+ temper->i2c_bit.setscl = temper_setscl;
+ temper->i2c_bit.getsda = temper_getsda;
+ temper->i2c_bit.getscl = NULL;
+ temper->i2c_bit.udelay = 50; /* in ms */
+ temper->i2c_bit.timeout = 1000; /* in ms */
+
+ temper->adap.timeout = 1000;
+ temper->adap.retries = 3;
+ strncpy(&temper->adap.name[0], "ch341-i2c", sizeof(temper->adap.name));
+ temper->adap.algo_data = &temper->i2c_bit;
+ i2c_bit_add_bus(&temper->adap);
/* Read config */
cfg = 0;
- err = temp_cfg_read(&adap, &cfg);
+ err = temp_cfg_read(&temper->adap, &cfg);
if (err < 0) {
- fprintf(stderr, "%s: Can't get current configuration.\n", argv[0]);
+ fprintf(stderr, "%s: Can't get current configuration.\n", usense_device_name(dev));
+ ch341_close(ch);
+ return -EINVAL;
}
if (cfg != 0x60) {
- err = temp_cfg_write(&adap, 0x60);
- }
- if (err < 0) {
- fprintf(stderr, "%s: Can't configure 12bit resolution\n", argv[0]);
+ err = temp_cfg_write(&temper->adap, 0x60);
}
-
- /* Dump temps */
- err = temp_read(&adap, REG_TEMP, &temp);
if (err < 0) {
- fprintf(stderr, "%s: Can't read temperator\n", argv[0]);
+ fprintf(stderr, "%s: Can't configure 12bit resolution\n", usense_device_name(dev));
+ ch341_close(ch);
+ return -EINVAL;
} else {
- /* Gotemp style */
- double celsius = temp / 256.0;
- printf("%f\n", celsius);
+ usense_prop_set(dev, "resolution","12");
}
+ TEMPer_update(dev, temper);
- ch341_close(ch);
+ *priv = temper;
- return EXIT_SUCCESS;
+ return 0;
}
+
+static void TEMPer_release(void *priv)
+{
+ struct temper *temper = priv;
+
+ ch341_close(temper->ch);
+ free(temper);
+}
+
+static int TEMPer_match(struct usb_device_descriptor *desc)
+{
+ return (desc->idVendor == 0x4348 &&
+ desc->idProduct == 0x5523 &&
+ desc->iManufacturer == 0 &&
+ desc->iProduct == 2 &&
+ desc->iSerialNumber == 0 &&
+ desc->bNumConfigurations == 1);
+}
+
+const struct usense_probe _usense_probe_TEMPer = {
+ .type = USENSE_PROBE_USB,
+ .probe = { .usb = { .match = TEMPer_match, .attach = TEMPer_attach, } },
+ .release = TEMPer_release,
+ .update = TEMPer_update,
+};
View
54 src/ch341.c
@@ -207,59 +207,17 @@ static int ch341_configure(struct ch341 *priv)
return r;
}
-static struct ch341 *ch341_acquire(int index)
+static struct ch341 *ch341_acquire(struct usb_dev_handle *usb)
{
- struct usb_bus *busses, *bus;
struct ch341 *priv;
- int err;
-
- usb_init();
- usb_find_busses();
- usb_find_devices();
-
- busses = usb_get_busses();
-
- for (bus = busses; bus != NULL; bus = bus->next) {
- struct usb_device *dev;
- struct usb_dev_handle *usb;
- for (dev = bus->devices; dev != NULL; dev = dev->next) {
- if (dev->descriptor.idVendor == 0x4348 &&
- dev->descriptor.idProduct == 0x5523 &&
- dev->descriptor.iManufacturer == 0 &&
- dev->descriptor.iProduct == 2 &&
- dev->descriptor.iSerialNumber == 0 &&
- dev->descriptor.bNumConfigurations == 1) {
-
- usb = usb_open(dev);
- if (usb == NULL) {
- fprintf(stderr, "Can't open device: %s\n", usb_strerror());
- continue;
- }
- if (index <= 0) {
- err = usb_clear_halt(usb, 0);
- err = usb_detach_kernel_driver_np(usb, 0);
- err = usb_claim_interface(usb, 0);
- if (err < 0) {
- fprintf(stderr, "Can't claim device: %s\n", usb_strerror());
- usb_close(usb);
- return NULL;
- }
- priv = calloc(1, sizeof(*priv));
- priv->dev = usb;
- return priv;
- }
- index--;
- }
- }
- }
- return NULL;
+ priv = calloc(1, sizeof(*priv));
+ priv->dev = usb;
+ return priv;
}
static void ch341_release(struct ch341 *priv)
{
- usb_reset(priv->dev);
- usb_close(priv->dev);
free(priv);
}
@@ -273,12 +231,12 @@ void ch341_close(struct ch341 *priv)
/* open this device, set default parameters */
-struct ch341 *ch341_open(int id)
+struct ch341 *ch341_open(struct usb_dev_handle *usb)
{
struct ch341 *priv;
int r;
- priv = ch341_acquire(id);
+ priv = ch341_acquire(usb);
if (priv == NULL) {
return NULL;
}
View
2 src/ch341.h
@@ -43,7 +43,7 @@
struct ch341;
-struct ch341 *ch341_open(int id);
+struct ch341 *ch341_open(struct usb_dev_handle *usb);
void ch341_close(struct ch341 *priv);
void ch341_set_termios(struct ch341 *priv, struct termios *termios, struct termios *old_termios);
View
201 src/gotemp.c
@@ -20,127 +20,63 @@
#include <usb.h>
+#include "usense.h"
+#include "units.h"
+
struct gotemp {
usb_dev_handle *usb;
+ struct usense_device *dev;
/* This is close to the structure I found in Greg's Code
* NOTE: This is in little endian format!
*/
struct packet {
- unsigned char measurements;
- unsigned char counter;
- int16_t measurement0;
- int16_t measurement1;
- int16_t measurement2;
+ unsigned char measurements;
+ unsigned char counter;
+ int16_t measurement0;
+ int16_t measurement1;
+ int16_t measurement2;
} __attribute__((packed)) packet;
struct {
- double offset;
- double ratio;
+ double add;
+ double mult;
} calibrate;
};
static void gotemp_calibrate(struct gotemp *gotemp)
{
- FILE *inf;
char buff[PATH_MAX];
+ const char *cp;
+ int err;
- gotemp->calibrate.offset = 0.0;
- gotemp->calibrate.ratio = 1.0;
-
- snprintf(buff, sizeof(buff), "%s/.gotemprc", getenv("HOME"));
- inf = fopen(buff, "r");
- if (inf == NULL) {
- inf = fopen("/etc/gotemp", "r");
- }
+ gotemp->calibrate.add = 0.0;
+ gotemp->calibrate.mult = 1.0;
- if (inf == NULL) {
- return;
+ err = usense_prop_get(gotemp->dev, "calibrate.add", buff, sizeof(buff));
+ if (err >= 0) {
+ gotemp->calibrate.add = strtod(cp, NULL);
}
- while (fgets(buff, sizeof(buff), inf) != NULL) {
- char *cp;
-
- cp = strchr(buff, '=');
- if (cp == NULL) {
- continue;
- }
- *(cp++) = 0;
- if (strcmp(buff, "calibrate.offset") == 0) {
- gotemp->calibrate.offset = strtod(cp, NULL);
- } else if (strcmp(buff, "calibrate.ratio") == 0) {
- gotemp->calibrate.ratio = strtod(cp, NULL);
- }
- }
-
- fclose(inf);
-}
-
-struct gotemp *gotemp_acquire(int index)
-{
- struct usb_bus *busses, *bus;
- struct gotemp *gotemp;
- int err;
-
- usb_init();
- usb_find_busses();
- usb_find_devices();
-
- busses = usb_get_busses();
-
- for (bus = busses; bus != NULL; bus = bus->next) {
- struct usb_device *dev;
- struct usb_dev_handle *usb;
-
- for (dev = bus->devices; dev != NULL; dev = dev->next) {
- if (dev->descriptor.idVendor == 0x08f7 &&
- dev->descriptor.idProduct == 0x0002 &&
- dev->descriptor.iManufacturer == 1 &&
- dev->descriptor.iProduct == 2 &&
- dev->descriptor.iSerialNumber == 0 &&
- dev->descriptor.bNumConfigurations == 1) {
-
- usb = usb_open(dev);
- if (usb == NULL) {
- fprintf(stderr, "Can't open Vernier EasyTemp (GoTemp) device: %s\n", usb_strerror());
- continue;
- }
- if (index <= 0) {
- err = usb_clear_halt(usb, 0);
- err = usb_detach_kernel_driver_np(usb, 0);
- err = usb_claim_interface(usb, 0);
- if (err < 0) {
- fprintf(stderr, "Can't claim Vernier EasyTemp (GoTemp) device: %s\n", usb_strerror());
- usb_close(usb);
- return NULL;
- }
- gotemp = calloc(1, sizeof(*gotemp));
- gotemp->usb = usb;
- gotemp_calibrate(gotemp);
- return gotemp;
- }
- index--;
- }
- }
+ err = usense_prop_get(gotemp->dev, "calibrate.mult", buff, sizeof(buff));
+ if (err >= 0) {
+ gotemp->calibrate.mult = strtod(cp, NULL);
}
-
- return NULL;
}
-void gotemp_release(struct gotemp *gotemp)
+void gotemp_release(void *priv)
{
- assert(gotemp != NULL);
- assert(gotemp->usb != NULL);
-
- usb_reset(gotemp->usb);
- usb_close(gotemp->usb);
+ struct gotemp *gotemp = priv;
free(gotemp);
}
-int gotemp_read(struct gotemp *gotemp, double *temp)
+int gotemp_update(struct usense_device *dev, void *priv)
{
/* From the GoIO_SDK */
const double conversion = 0.0078125;
+ struct gotemp *gotemp = priv;
+ uint64_t mkelvin;
+ char buff[64];
int len;
assert(sizeof(gotemp->packet) == 8);
@@ -151,57 +87,52 @@ int gotemp_read(struct gotemp *gotemp, double *temp)
if (len == -EAGAIN) {
continue;
}
- return -1;
+ return -EINVAL;
}
} while (0);
- *temp = (((double) gotemp->packet.measurement0) * conversion * gotemp->calibrate.ratio) + gotemp->calibrate.offset;
- return 0;
+ mkelvin = (uint64_t)(C_TO_K((((double) gotemp->packet.measurement0) * conversion * gotemp->calibrate.mult) + gotemp->calibrate.add) * 1000000);
+ snprintf(buff, sizeof(buff), "%llu", (unsigned long long)mkelvin);
+ return usense_prop_set(dev, "reading", buff);
}
-/* Function to convert Celsius to Fahrenheit */
-float CtoF(float C)
+static int gotemp_attach(struct usense_device *dev, struct usb_dev_handle *usb, void **priv)
{
- return (C * 9.0 / 5.0) + 32;
+ int err;
+ struct gotemp *gotemp;
+
+ gotemp = calloc(1, sizeof(*gotemp));
+ gotemp->usb = usb;
+ gotemp->dev = dev;
+ gotemp_calibrate(gotemp);
+
+ if (gotemp == NULL) {
+ return -ENODEV;
+ }
+
+ /* Do a dummy update first */
+ gotemp_update(dev, gotemp);
+
+ err = gotemp_update(dev, gotemp);
+ if (err < 0) {
+ gotemp_release(gotemp);
+ return err;
+ }
}
-int main(int argc, char **argv)
+static int gotemp_match(struct usb_device_descriptor *desc)
{
- double temp;
- int err;
- enum { TEMP_F, TEMP_C } temp_mode = TEMP_F;
- struct gotemp *usb;
-
- if (argc > 1) {
- if (argc==2 && strcmp(argv[1],"-F")==0) {
- temp_mode = TEMP_F;
- } else if (argc==2 && strcmp(argv[1],"-C")==0) {
- temp_mode = TEMP_C;
- }
- }
-
- usb = gotemp_acquire(0);
- if (usb == NULL) {
- return EXIT_FAILURE;
- }
-
- err = gotemp_read(usb, &temp);
- err = gotemp_read(usb, &temp);
- gotemp_release(usb);
-
- if (err < 0) {
- fprintf(stderr, "Can't get a reading.\n");
- return EXIT_FAILURE;
- }
-
- switch (temp_mode) {
- case TEMP_F:
- printf("%.2f\n", CtoF(temp));
- break;
- case TEMP_C:
- printf("%.2f\n", temp);
- break;
- }
-
- return 0;
+ return (desc->idVendor == 0x08f7 &&
+ desc->idProduct == 0x0002 &&
+ desc->iManufacturer == 1 &&
+ desc->iProduct == 2 &&
+ desc->iSerialNumber == 0 &&
+ desc->bNumConfigurations == 1);
}
+
+const struct usense_probe _usense_probe_gotemp = {
+ .type = USENSE_PROBE_USB,
+ .probe = { .usb = { .match = gotemp_match, .attach = gotemp_attach, } },
+ .release = gotemp_release,
+ .update = gotemp_update,
+};
View
128 src/main.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2009, Netronome Systems
+ * Author: Jason McMullan <jason.mcmullan@netronome.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "usense.h"
+
+int list_devices(void)
+{
+ struct usense *usense;
+ struct usense_device *dev;
+
+ usense = usense_start(0, NULL);
+ if (usense == NULL) {
+ return EXIT_SUCCESS;
+ }
+
+ for (dev = usense_first(usense); dev != NULL; dev = usense_next(usense, dev)) {
+ printf("%s\n", usense_device_name(dev));
+ }
+
+ return 0;
+
+ usense_stop(usense);
+}
+
+int list_device_props(const char *dev_name)
+{
+ struct usense_device *dev;
+ char value[PATH_MAX];
+ const char *key;
+ int err;
+
+ dev = usense_open(dev_name);
+ if (dev == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ for (key = usense_prop_first(dev); key != NULL; key = usense_prop_next(dev, key)) {
+ err = usense_prop_get(dev, key, value, sizeof(value));
+ if (err < 0) {
+ continue;
+ }
+ printf("%s=%s\n", key, value);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+int show_device_prop(const char *dev_name, const char *prop_name)
+{
+ struct usense_device *dev;
+ char value[PATH_MAX];
+ int err;
+
+ dev = usense_open(dev_name);
+ if (dev == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ err = usense_prop_get(dev, prop_name, value, sizeof(value));
+ if (err < 0) {
+ return EXIT_FAILURE;
+ }
+ printf("%s\n", value);
+
+ return EXIT_SUCCESS;
+}
+
+int set_device_prop(const char *dev_name, const char *prop_name, const char *value)
+{
+ struct usense_device *dev;
+ int err;
+
+ dev = usense_open(dev_name);
+ if (dev == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ err = usense_prop_set(dev, prop_name, value);
+ if (err < 0) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc == 1) {
+ /* List all */
+ return list_devices();
+ }
+
+ if (argc == 2) {
+ /* List props of a device */
+ return list_device_props(argv[1]);
+ }
+
+ if (argc == 3) {
+ /* List single prop of a device */
+ return show_device_prop(argv[1], argv[2]);
+ }
+
+ if (argc == 4) {
+ /* Set prop of a device */
+ return set_device_prop(argv[1], argv[2], argv[3]);
+ }
+
+ return EXIT_FAILURE;
+}
View
28 src/units.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2009, Jason S. McMullan
+ * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef UNITS_H
+#define UNITS_H
+
+#define C_TO_K(x) ((x) + 273.15)
+#define C_TO_F(x) ((x) * 9.0 / 5.0 + 32)
+#define K_TO_F(x) (((x) - 273.15) * 9.0 / 5.0 + 32)
+
+#endif /* UNITS_H */
View
421 src/usense.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2009, Jason S. McMullan
+ * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <usb.h>
+
+#include "usense.h"
+
+struct usense_prop {
+ const char *key;
+ char *value;
+};
+
+struct usense_device {
+ struct usense_device *next, **pprev;
+ char *name;
+ const struct usense_probe *probe;
+ void *priv;
+ struct usense_prop *prop; /* bsearch */
+ int props;
+ void *handle; /* device type handle */
+};
+
+struct usense {
+ int fd; /* Reading FD */
+ struct usense_device *devices;
+};
+
+static const struct usense_probe **dev_probe;
+static int dev_probes;
+
+extern const struct usense_probe _usense_probe_gotemp, _usense_probe_TEMPer;
+
+/* Register device to look for
+ */
+int usense_probe_register(const struct usense_probe *probe)
+{
+ int i;
+ for (i = 0; i < dev_probes; i++) {
+ if (dev_probe[i] == probe) {
+ return -EEXIST;
+ }
+ }
+
+ dev_probe = realloc(dev_probe, sizeof(*dev_probe) * (dev_probes + 1));
+ assert(dev_probe != NULL);
+ dev_probe[dev_probes++] = probe;
+}
+
+void usense_probe_unregister(const struct usense_probe *probe)
+{
+ int i;
+ for (i = 0; i < dev_probes; i++) {
+ if (dev_probe[i] == probe) {
+ break;
+ }
+ }
+
+ if (i == dev_probes) {
+ return;
+ }
+
+ memcpy(&dev_probe[i], &dev_probe[i+1], sizeof(*dev_probe) * (dev_probes - i - 1));
+ dev_probes--;
+}
+
+static int usense_init(void)
+{
+ if (dev_probes == 0) {
+ usense_probe_register(&_usense_probe_gotemp);
+ usense_probe_register(&_usense_probe_TEMPer);
+ }
+}
+
+
+/************** Persistent daemon mode ****************
+ *
+ * Set devices to -1 for auto-detection.
+ *
+ * Device names are of the form:
+ *
+ * usb:<bus>.<device>
+ *
+ */
+static struct usense *usense_new(void)
+{
+ struct usense *usense;
+
+ usense_init();
+
+ usense = calloc(1, sizeof(*usense));
+ usense->fd = -1;
+
+ return usense;
+}
+
+static void usense_free(struct usense *usense)
+{
+ free(usense);
+}
+
+struct usense *usense_start(int devices, const char **device_names)
+{
+ struct usense *usense;
+
+ usense = usense_new();
+
+ usense_detect(usense);
+
+ /* TODO */
+
+ return usense;
+}
+
+void usense_stop(struct usense *usense)
+{
+ usense_free(usense);
+}
+
+static struct usense_device *usense_device_new(struct usense *usense, const char *name, const struct usense_probe *probe, void *handle)
+{
+ struct usense_device *dev;
+
+ dev = calloc(1, sizeof(*dev));
+
+ dev->next = usense->devices;
+ dev->name = strdup(name);
+ dev->pprev = &usense->devices;
+ usense->devices = dev;
+ if (dev->next != NULL) {
+ dev->next->pprev = &dev->next;
+ }
+ dev->handle = handle;
+ dev->probe = probe;
+
+ usense_prop_set(dev, "reading", "unknown");
+
+ return dev;
+}
+
+static int usense_prop_cmp(const void *a, const void *b)
+{
+ const struct usense_prop *prop_a = a, *prop_b = b;
+
+ return strcmp(prop_a->key, prop_b->key);
+}
+
+static void usense_device_free(struct usense_device *dev)
+{
+ if (dev->next != NULL) {
+ dev->next->pprev = dev->pprev;
+ }
+ *dev->pprev = dev->next;
+
+ switch (dev->probe->type) {
+ case USENSE_PROBE_INVALID: break;
+ case USENSE_PROBE_USB: usb_close(dev->handle); break;
+ }
+
+ free(dev->prop);
+ free(dev->name);
+ free(dev);
+}
+
+static struct usense_device *usense_probe_usb(struct usense *usense, struct usb_device *dev)
+{
+ int i;
+ struct usense_device *udev = NULL;
+ char name[PATH_MAX];
+
+ for (i = 0; i < dev_probes; i++) {
+ struct usb_dev_handle *usb;
+ int err;
+
+ if (dev_probe[i]->type != USENSE_PROBE_USB)
+ continue;
+ if (!dev_probe[i]->probe.usb.match(&dev->descriptor))
+ continue;
+
+ usb = usb_open(dev);
+ if (usb == NULL) {
+ continue;
+ }
+ err = usb_clear_halt(usb, 0);
+ err = usb_detach_kernel_driver_np(usb, 0);
+ err = usb_claim_interface(usb, 0);
+ if (err < 0) {
+ usb_close(usb);
+ continue;
+ }
+
+ snprintf(name, sizeof(name), "usb:%d.%d", dev->bus->location, dev->devnum);
+ udev = usense_device_new(usense, name, dev_probe[i], usb);
+ err = dev_probe[i]->probe.usb.attach(udev, usb, &udev->priv);
+ if (err < 0) {
+ usense_device_free(udev);
+ continue;
+ }
+
+ break;
+ }
+
+ return udev;
+}
+
+static int usb_is_initted = 0;
+
+static struct usb_device *usb_find(int bus_id, int dev_id)
+{
+ struct usb_bus *busses, *bus;
+
+ if (!usb_is_initted) {
+ usb_init();
+ usb_is_initted = 1;
+ }
+
+ usb_find_busses();
+ usb_find_devices();
+
+ busses = usb_get_busses();
+
+ for (bus = busses; bus != NULL; bus = bus->next) {
+ struct usb_device *dev;
+
+ if (bus->location != bus_id)
+ continue;
+
+ for (dev = bus->devices; dev != NULL; dev = dev->next) {
+ if (dev->devnum == dev_id)
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Rescan for new devices.
+ */
+void usense_detect(struct usense *usense)
+{
+ struct usb_bus *busses, *bus;
+
+ if (!usb_is_initted) {
+ usb_init();
+ usb_is_initted = 1;
+ }
+
+ usb_find_busses();
+ usb_find_devices();
+
+ busses = usb_get_busses();
+
+ for (bus = busses; bus != NULL; bus = bus->next) {
+ struct usb_device *dev;
+ struct usb_dev_handle *usb;
+ for (dev = bus->devices; dev != NULL; dev = dev->next) {
+ usense_probe_usb(usense, dev);
+ }
+ }
+}
+
+/* Walk the device list.
+ */
+struct usense_device *usense_first(struct usense *usense)
+{
+ return usense->devices;
+}
+
+struct usense_device *usense_next(struct usense *usense, struct usense_device *curr_dev)
+{
+ return curr_dev->next;
+}
+
+/*
+ * fd to use with poll(2) for monitoring when device
+ * properties have changed
+ */
+int usense_monitor_fd(struct usense *usense)
+{
+ return usense->fd;
+}
+
+/************** Use-once mode ****************
+ */
+struct usense_device *usense_open(const char *device_name)
+{
+ static struct usense *usense = NULL;
+
+ if (usense == NULL) {
+ usense = usense_new();
+ }
+
+ if (strncmp(device_name, "usb:", 4) == 0) {
+ struct usb_device *udev;
+ int err, bus_no, dev_no, len;
+
+ err = sscanf(device_name,"usb:%d.%d%n", &bus_no, &dev_no, &len);
+ if (err != 2 || len != strlen(device_name)) {
+ return NULL;
+ }
+
+ udev = usb_find(bus_no, dev_no);
+ return usense_probe_usb(usense, udev);
+ }
+
+ return NULL;
+}
+
+
+void usense_close(struct usense_device *dev)
+{
+ usense_device_free(dev);
+}
+
+/************** General get/set **************/
+
+const char *usense_device_name(struct usense_device *dev)
+{
+ return dev->name;
+}
+
+
+/* Get property from device
+ * (always returns in UTF8z format)
+ *
+ * The only 'guaranteed' to exists property is "reading", which is returned in
+ * microkelvin units.
+ */
+int usense_prop_get(struct usense_device *dev, const char *key, char *buff, size_t len)
+{
+ struct usense_prop *prop, match;
+
+ match.key = key;
+ prop = bsearch(&match, dev->prop, dev->props, sizeof(*prop), usense_prop_cmp);
+
+ if (prop == NULL) {
+ return -ENOENT;
+ }
+
+ strncpy(buff, prop->value, len);
+ if (len > 0) {
+ buff[len - 1] = 0;
+ }
+
+ return 0;
+}
+
+int usense_prop_set(struct usense_device *dev, const char *key, const char *value)
+{
+ struct usense_prop *prop, match;
+
+ match.key = key;
+ prop = bsearch(&match, dev->prop, dev->props, sizeof(*prop), usense_prop_cmp);
+ if (prop == NULL) {
+ dev->prop = realloc(dev->prop, sizeof(*prop) * (dev->props+1));
+ prop = &dev->prop[dev->props++];
+ prop->key = strdup(key);
+ prop->value = strdup(value);
+ return 1;
+ }
+
+ if (strcmp(prop->value, value) != 0) {
+ free(prop->value);
+ prop->value = strdup(value);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Property walking
+ */
+const char *usense_prop_first(struct usense_device *dev)
+{
+ if (dev->props == 0) {
+ return NULL;
+ }
+
+ return dev->prop[0].key;
+}
+
+const char *usense_prop_next(struct usense_device *dev, const char *curr_prop)
+{
+ struct usense_prop *prop, match;
+
+ match.key = curr_prop;
+ prop = bsearch(&match, dev->prop, dev->props, sizeof(*prop), usense_prop_cmp);
+ if (prop == NULL) {
+ return NULL;
+ }
+
+ prop++;
+ if ((prop - dev->prop) > dev->props) {
+ return NULL;
+ }
+
+ return prop->key;
+}
View
121 src/usense.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2009, Jason S. McMullan
+ * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef USENSE_H
+#define USENSE_H
+
+#include <usb.h>
+
+struct usense;
+struct usense_device;
+
+struct usense_probe {
+ enum {
+ USENSE_PROBE_INVALID=0,
+ USENSE_PROBE_USB,
+ USENSE_PROBE_SERIAL,
+ } type;
+
+ union {
+ struct { /* USB probe functions */
+ /* Does it look like one? 0 = no, 1 = yes */
+ int (*match)(struct usb_device_descriptor *desc);
+
+ /* Set up '*priv' to point to any drive data you need.
+ */
+ int (*attach)(struct usense_device *dev, struct usb_dev_handle *usb, void **priv);
+ } usb;
+ struct { /* Serial probe functions */
+ /* Does it look like one? 0 = no, 1 = yes */
+ int (*match)(int tty_fd);
+
+ /* Set up '*priv' to point to any drive data you need.
+ */
+ int (*attach)(struct usense_device *dev, int tty_fd, void **priv);
+ } serial;
+ } probe;
+
+ /* Free private data */
+ void (*release)(void *priv);
+
+ /* Update the usense_device */
+ int (*update)(struct usense_device *dev, void *priv);
+};
+
+/* Register devices to look for
+ */
+int usense_probe_register(const struct usense_probe *ops);
+void usense_probe_unregister(const struct usense_probe *ops);
+
+/************** Persistent daemon mode ****************
+ *
+ * Set devices to -1 for auto-detection.
+ *
+ * Device names are of the form:
+ *
+ * usb:<bus>.<device>
+ *
+ */
+struct usense *usense_start(int devices, const char **device_names);
+void usense_stop(struct usense *usense);
+
+/*
+ * Rescan for new devices.
+ */
+void usense_detect(struct usense *usense);
+
+/* Walk the device list.
+ */
+struct usense_device *usense_first(struct usense *usense);
+struct usense_device *usense_next(struct usense *usense, struct usense_device *curr_dev);
+
+/*
+ * fd to use with poll(2) for monitoring when device
+ * properties have changed
+ */
+int usense_monitor_fd(struct usense *usense);
+
+/************** Use-once mode ****************
+ */
+struct usense_device *usense_open(const char *device_name);
+
+void usense_close(struct usense_device *dev);
+
+/************** General get/set **************/
+
+const char *usense_device_name(struct usense_device *dev);
+
+/* Get property from device
+ * (always returns in UTF8z format)
+ *
+ * The only 'guaranteed' to exists property is "reading", which is returned in
+ * microkelvin units.
+ */
+int usense_prop_get(struct usense_device *dev, const char *prop, char *buff, size_t len);
+
+int usense_prop_set(struct usense_device *dev, const char *prop, const char *value);
+
+/* Property walking
+ */
+const char *usense_prop_first(struct usense_device *dev);
+const char *usense_prop_next(struct usense_device *dev, const char *curr_prop);
+
+
+#endif /* USENSE_H */

0 comments on commit b867bb7

Please sign in to comment.