/* * Copyright © 2009 Keith Packard * * 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 #include #include #include #include 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 = strtol(arg, &end, 0); if (end != arg) { if (*end != '\0') return NULL; product = NULL; } else { /* check for : */ 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; }