Initial Commit - Copy from Altus Metrum AltOS
This commit is contained in:
338
ao-tools/lib/cc-usbdev.c
Normal file
338
ao-tools/lib/cc-usbdev.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright © 2009 Keith Packard <keithp@keithp.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.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "cc.h"
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static char *
|
||||
load_string(char *dir, char *file)
|
||||
{
|
||||
char *full = cc_fullname(dir, file);
|
||||
char line[4096];
|
||||
char *r;
|
||||
FILE *f;
|
||||
int rlen;
|
||||
|
||||
f = fopen(full, "r");
|
||||
free(full);
|
||||
if (!f)
|
||||
return NULL;
|
||||
r = fgets(line, sizeof (line), f);
|
||||
fclose(f);
|
||||
if (!r)
|
||||
return NULL;
|
||||
rlen = strlen(r);
|
||||
if (r[rlen-1] == '\n')
|
||||
r[rlen-1] = '\0';
|
||||
return strdup(r);
|
||||
}
|
||||
|
||||
static int
|
||||
load_hex(char *dir, char *file)
|
||||
{
|
||||
char *line;
|
||||
char *end;
|
||||
long i;
|
||||
|
||||
line = load_string(dir, file);
|
||||
if (!line)
|
||||
return -1;
|
||||
i = strtol(line, &end, 16);
|
||||
free(line);
|
||||
if (end == line)
|
||||
return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
load_dec(char *dir, char *file)
|
||||
{
|
||||
char *line;
|
||||
char *end;
|
||||
long i;
|
||||
|
||||
line = load_string(dir, file);
|
||||
if (!line)
|
||||
return -1;
|
||||
i = strtol(line, &end, 10);
|
||||
free(line);
|
||||
if (end == line)
|
||||
return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
dir_filter_tty_colon(const struct dirent *d)
|
||||
{
|
||||
return strncmp(d->d_name, "tty:", 4) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dir_filter_tty(const struct dirent *d)
|
||||
{
|
||||
return strncmp(d->d_name, "tty", 3) == 0;
|
||||
}
|
||||
|
||||
static char *
|
||||
usb_tty(char *sys)
|
||||
{
|
||||
char *base;
|
||||
int num_configs;
|
||||
int config;
|
||||
struct dirent **namelist;
|
||||
int interface;
|
||||
int num_interfaces;
|
||||
char endpoint_base[64];
|
||||
char *endpoint_full;
|
||||
char *tty_dir;
|
||||
int ntty;
|
||||
char *tty;
|
||||
|
||||
base = cc_basename(sys);
|
||||
num_configs = load_hex(sys, "bNumConfigurations");
|
||||
num_interfaces = load_hex(sys, "bNumInterfaces");
|
||||
for (config = 1; config <= num_configs; config++) {
|
||||
for (interface = 0; interface < num_interfaces; interface++) {
|
||||
sprintf(endpoint_base, "%s:%d.%d",
|
||||
base, config, interface);
|
||||
endpoint_full = cc_fullname(sys, endpoint_base);
|
||||
|
||||
/* Check for tty:ttyACMx style names
|
||||
*/
|
||||
ntty = scandir(endpoint_full, &namelist,
|
||||
dir_filter_tty_colon,
|
||||
alphasort);
|
||||
if (ntty > 0) {
|
||||
free(endpoint_full);
|
||||
tty = cc_fullname("/dev", namelist[0]->d_name + 4);
|
||||
free(namelist);
|
||||
return tty;
|
||||
}
|
||||
|
||||
/* Check for tty/ttyACMx style names
|
||||
*/
|
||||
tty_dir = cc_fullname(endpoint_full, "tty");
|
||||
ntty = scandir(tty_dir, &namelist,
|
||||
dir_filter_tty,
|
||||
alphasort);
|
||||
free (tty_dir);
|
||||
if (ntty > 0) {
|
||||
tty = cc_fullname("/dev", namelist[0]->d_name);
|
||||
free(endpoint_full);
|
||||
free(namelist);
|
||||
return tty;
|
||||
}
|
||||
|
||||
/* Check for ttyACMx style names
|
||||
*/
|
||||
ntty = scandir(endpoint_full, &namelist,
|
||||
dir_filter_tty,
|
||||
alphasort);
|
||||
free(endpoint_full);
|
||||
if (ntty > 0) {
|
||||
tty = cc_fullname("/dev", namelist[0]->d_name);
|
||||
free(namelist);
|
||||
return tty;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct cc_usbdev *
|
||||
usb_scan_device(char *sys)
|
||||
{
|
||||
struct cc_usbdev *usbdev;
|
||||
|
||||
usbdev = calloc(1, sizeof (struct cc_usbdev));
|
||||
if (!usbdev)
|
||||
return NULL;
|
||||
usbdev->sys = strdup(sys);
|
||||
usbdev->manufacturer = load_string(sys, "manufacturer");
|
||||
usbdev->product = load_string(sys, "product");
|
||||
usbdev->serial = load_dec(sys, "serial");
|
||||
usbdev->idProduct = load_hex(sys, "idProduct");
|
||||
usbdev->idVendor = load_hex(sys, "idVendor");
|
||||
usbdev->tty = usb_tty(sys);
|
||||
return usbdev;
|
||||
}
|
||||
|
||||
static void
|
||||
usbdev_free(struct cc_usbdev *usbdev)
|
||||
{
|
||||
free(usbdev->sys);
|
||||
free(usbdev->manufacturer);
|
||||
free(usbdev->product);
|
||||
/* this can get used as a return value */
|
||||
if (usbdev->tty)
|
||||
free(usbdev->tty);
|
||||
free(usbdev);
|
||||
}
|
||||
|
||||
#define USB_DEVICES "/sys/bus/usb/devices"
|
||||
|
||||
static int
|
||||
dir_filter_dev(const struct dirent *d)
|
||||
{
|
||||
const char *n = d->d_name;
|
||||
char c;
|
||||
|
||||
while ((c = *n++)) {
|
||||
if (isdigit(c))
|
||||
continue;
|
||||
if (c == '-')
|
||||
continue;
|
||||
if (c == '.' && n != d->d_name + 1)
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
is_am(int idVendor, int idProduct) {
|
||||
if (idVendor == 0xfffe)
|
||||
return 1;
|
||||
if (idVendor == 0x0403 && idProduct == 0x6015)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cc_usbdevs *
|
||||
cc_usbdevs_scan(int non_tty)
|
||||
{
|
||||
int e;
|
||||
struct dirent **ents;
|
||||
char *dir;
|
||||
struct cc_usbdev *dev;
|
||||
struct cc_usbdevs *devs;
|
||||
int n;
|
||||
|
||||
devs = calloc(1, sizeof (struct cc_usbdevs));
|
||||
if (!devs)
|
||||
return NULL;
|
||||
|
||||
n = scandir (USB_DEVICES, &ents,
|
||||
dir_filter_dev,
|
||||
alphasort);
|
||||
if (!n)
|
||||
return 0;
|
||||
for (e = 0; e < n; e++) {
|
||||
dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
|
||||
dev = usb_scan_device(dir);
|
||||
free(dir);
|
||||
if (is_am(dev->idVendor, dev->idProduct) && (non_tty || dev->tty)) {
|
||||
if (devs->dev)
|
||||
devs->dev = realloc(devs->dev,
|
||||
(devs->ndev + 1) * sizeof (struct usbdev *));
|
||||
else
|
||||
devs->dev = malloc (sizeof (struct usbdev *));
|
||||
devs->dev[devs->ndev++] = dev;
|
||||
}
|
||||
}
|
||||
free(ents);
|
||||
return devs;
|
||||
}
|
||||
|
||||
void
|
||||
cc_usbdevs_free(struct cc_usbdevs *usbdevs)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!usbdevs)
|
||||
return;
|
||||
for (i = 0; i < usbdevs->ndev; i++)
|
||||
usbdev_free(usbdevs->dev[i]);
|
||||
free(usbdevs);
|
||||
}
|
||||
|
||||
static char *
|
||||
match_dev(char *product, int serial)
|
||||
{
|
||||
struct cc_usbdevs *devs;
|
||||
struct cc_usbdev *dev;
|
||||
int i;
|
||||
char *tty = NULL;
|
||||
|
||||
devs = cc_usbdevs_scan(FALSE);
|
||||
if (!devs)
|
||||
return NULL;
|
||||
for (i = 0; i < devs->ndev; i++) {
|
||||
dev = devs->dev[i];
|
||||
if (product && strncmp (product, dev->product, strlen(product)) != 0)
|
||||
continue;
|
||||
if (serial && serial != dev->serial)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i < devs->ndev) {
|
||||
tty = devs->dev[i]->tty;
|
||||
devs->dev[i]->tty = NULL;
|
||||
}
|
||||
cc_usbdevs_free(devs);
|
||||
return tty;
|
||||
}
|
||||
|
||||
char *
|
||||
cc_usbdevs_find_by_arg(char *arg, char *default_product)
|
||||
{
|
||||
char *product;
|
||||
int serial;
|
||||
char *end;
|
||||
char *colon;
|
||||
char *tty;
|
||||
|
||||
if (arg)
|
||||
{
|
||||
/* check for <serial> */
|
||||
serial = strtol(arg, &end, 0);
|
||||
if (end != arg) {
|
||||
if (*end != '\0')
|
||||
return NULL;
|
||||
product = NULL;
|
||||
} else {
|
||||
/* check for <product>:<serial> */
|
||||
colon = strchr(arg, ':');
|
||||
if (colon) {
|
||||
product = strndup(arg, colon - arg);
|
||||
serial = strtol(colon + 1, &end, 0);
|
||||
if (*end != '\0')
|
||||
return NULL;
|
||||
} else {
|
||||
product = arg;
|
||||
serial = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
product = NULL;
|
||||
serial = 0;
|
||||
}
|
||||
tty = NULL;
|
||||
if (!product && default_product)
|
||||
tty = match_dev(default_product, serial);
|
||||
if (!tty)
|
||||
tty = match_dev(product, serial);
|
||||
if (product && product != arg)
|
||||
free(product);
|
||||
return tty;
|
||||
}
|
||||
Reference in New Issue
Block a user