2005-07-04 19:02:25 +04:00
/*
* usb_id . c
*
* Identify an USB ( block ) device
*
* Copyright ( c ) 2005 SUSE Linux Products GmbH , Germany
2005-09-27 18:27:35 +04:00
*
2005-07-04 19:02:25 +04:00
* Author :
* Hannes Reinecke < hare @ suse . de >
*
2005-09-27 18:27:35 +04:00
* 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 version 2 of the License .
2005-07-04 19:02:25 +04:00
*/
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <unistd.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
2007-03-21 15:55:48 +03:00
# include <getopt.h>
2005-07-04 19:02:25 +04:00
2006-01-09 23:18:00 +03:00
# include "../../udev.h"
2005-07-04 19:02:25 +04:00
2006-01-18 06:24:48 +03:00
# define MAX_PATH_LEN 512
2006-01-09 23:18:00 +03:00
# define MAX_SERIAL_LEN 256
2005-07-04 19:02:25 +04:00
# define BLKGETSIZE64 _IOR(0x12,114,size_t)
# ifdef USE_LOG
void log_message ( int priority , const char * format , . . . )
{
va_list args ;
static int udev_log = - 1 ;
if ( udev_log = = - 1 ) {
const char * value ;
value = getenv ( " UDEV_LOG " ) ;
if ( value )
udev_log = log_priority ( value ) ;
else
udev_log = LOG_ERR ;
}
if ( priority > udev_log )
return ;
va_start ( args , format ) ;
vsyslog ( priority , format , args ) ;
va_end ( args ) ;
}
# endif
static char vendor_str [ 64 ] ;
static char model_str [ 64 ] ;
static char serial_str [ MAX_SERIAL_LEN ] ;
2007-03-21 15:55:48 +03:00
static char revision_str [ 64 ] ;
static char type_str [ 64 ] ;
static char instance_str [ 64 ] ;
2005-07-04 19:02:25 +04:00
static int use_usb_info ;
2005-07-08 05:29:19 +04:00
static int use_num_info ;
2005-07-04 19:02:25 +04:00
2005-08-01 03:33:36 +04:00
static void set_str ( char * to , const char * from , size_t count )
2005-07-04 19:02:25 +04:00
{
2005-08-01 03:33:36 +04:00
size_t i , j , len ;
2005-07-04 19:02:25 +04:00
/* strip trailing whitespace */
len = strnlen ( from , count ) ;
2005-08-22 13:37:12 +04:00
while ( len & & isspace ( from [ len - 1 ] ) )
2005-07-04 19:02:25 +04:00
len - - ;
/* strip leading whitespace */
i = 0 ;
while ( isspace ( from [ i ] ) & & ( i < len ) )
i + + ;
j = 0 ;
while ( i < len ) {
/* substitute multiple whitespace */
if ( isspace ( from [ i ] ) ) {
while ( isspace ( from [ i ] ) )
i + + ;
to [ j + + ] = ' _ ' ;
}
/* Replace '/' with '.' */
if ( from [ i ] = = ' / ' ) {
to [ j + + ] = ' . ' ;
i + + ;
continue ;
}
/* skip non-printable chars */
if ( ! isalnum ( from [ i ] ) & & ! ispunct ( from [ i ] ) ) {
i + + ;
continue ;
}
to [ j + + ] = from [ i + + ] ;
}
to [ j ] = ' \0 ' ;
}
2006-01-23 21:09:17 +03:00
static void set_usb_iftype ( char * to , int if_class_num , size_t len )
2005-07-04 19:02:25 +04:00
{
2005-08-13 02:18:44 +04:00
char * type = " generic " ;
2005-07-04 19:02:25 +04:00
2006-01-23 21:09:17 +03:00
switch ( if_class_num ) {
case 1 :
type = " audio " ;
break ;
case 3 :
type = " hid " ;
break ;
case 7 :
type = " printer " ;
break ;
case 8 :
2007-03-21 15:55:48 +03:00
type = " storage " ;
2006-01-23 21:09:17 +03:00
break ;
case 2 : /* CDC-Control */
case 5 : /* Physical */
case 6 : /* Image */
case 9 : /* HUB */
case 0x0a : /* CDC-Data */
case 0x0b : /* Chip/Smart Card */
case 0x0d : /* Content Security */
case 0x0e : /* Video */
case 0xdc : /* Diagnostic Device */
case 0xe0 : /* Wireless Controller */
case 0xf2 : /* Application-specific */
case 0xff : /* Vendor-specific */
break ;
default :
break ;
2005-07-04 19:02:25 +04:00
}
2005-08-13 02:18:44 +04:00
strncpy ( to , type , len ) ;
to [ len - 1 ] = ' \0 ' ;
2005-07-04 19:02:25 +04:00
}
2006-01-23 21:09:17 +03:00
static int set_usb_mass_storage_ifsubtype ( char * to , const char * from , size_t len )
2005-07-04 19:02:25 +04:00
{
int type_num = 0 ;
char * eptr ;
2005-08-13 02:18:44 +04:00
char * type = " generic " ;
2005-07-04 19:02:25 +04:00
type_num = strtoul ( from , & eptr , 0 ) ;
if ( eptr ! = from ) {
switch ( type_num ) {
case 2 :
2005-08-13 02:18:44 +04:00
type = " cd " ;
2005-07-04 19:02:25 +04:00
break ;
case 3 :
2005-08-13 02:18:44 +04:00
type = " tape " ;
2005-07-04 19:02:25 +04:00
break ;
case 4 : /* UFI */
case 5 : /* SFF-8070i */
2005-08-13 02:18:44 +04:00
type = " floppy " ;
2005-07-04 19:02:25 +04:00
break ;
case 1 : /* RBC devices */
case 6 : /* Transparent SPC-2 devices */
2005-08-13 02:18:44 +04:00
type = " disk " ;
break ;
default :
2005-07-04 19:02:25 +04:00
break ;
}
}
2007-03-21 15:55:48 +03:00
strlcpy ( to , type , len ) ;
2005-07-04 19:02:25 +04:00
return type_num ;
}
2007-03-21 15:55:48 +03:00
static void set_scsi_type ( char * to , const char * from , size_t len )
2005-07-04 19:02:25 +04:00
{
int type_num ;
char * eptr ;
2007-03-21 15:55:48 +03:00
char * type = " generic " ;
2005-07-04 19:02:25 +04:00
type_num = strtoul ( from , & eptr , 0 ) ;
if ( eptr ! = from ) {
switch ( type_num ) {
case 0 :
2007-03-21 15:55:48 +03:00
case 0xe :
type = " disk " ;
2005-07-04 19:02:25 +04:00
break ;
case 1 :
2007-03-21 15:55:48 +03:00
type = " tape " ;
2005-07-04 19:02:25 +04:00
break ;
case 4 :
case 7 :
case 0xf :
2007-03-21 15:55:48 +03:00
type = " optical " ;
break ;
case 5 :
type = " cd " ;
2005-07-04 19:02:25 +04:00
break ;
default :
break ;
}
}
2007-03-21 15:55:48 +03:00
strlcpy ( to , type , len ) ;
2005-07-04 19:02:25 +04:00
}
/*
* A unique USB identification is generated like this :
*
* 1. ) Get the USB device type from DeviceClass , InterfaceClass
* and InterfaceSubClass
* 2. ) If the device type is ' Mass - Storage / SPC - 2 ' or ' Mass - Storage / RBC '
* use the SCSI vendor and model as USB - Vendor and USB - model .
* 3. ) Otherwise use the USB manufacturer and product as
* USB - Vendor and USB - model . Any non - printable characters
* in those strings will be skipped ; a slash ' / ' will be converted
* into a full stop ' . ' .
* 4. ) If that fails , too , we will use idVendor and idProduct
* as USB - Vendor and USB - model .
* 5. ) The USB identification is the USB - vendor and USB - model
* string concatenated with an underscore ' _ ' .
* 6. ) If the device supplies a serial number , this number
* is concatenated with the identification with an underscore ' _ ' .
*/
2006-01-09 23:18:00 +03:00
static int usb_id ( const char * devpath )
2005-07-04 19:02:25 +04:00
{
2006-01-09 23:18:00 +03:00
struct sysfs_device * dev ;
2006-01-18 06:24:48 +03:00
struct sysfs_device * dev_interface ;
struct sysfs_device * dev_usb ;
2006-01-09 23:18:00 +03:00
const char * scsi_model , * scsi_vendor , * scsi_type , * scsi_rev ;
const char * usb_model = NULL , * usb_vendor = NULL , * usb_rev , * usb_serial ;
const char * if_class , * if_subclass ;
2005-07-04 19:02:25 +04:00
int if_class_num ;
int protocol = 0 ;
2006-01-09 23:18:00 +03:00
dbg ( " devpath %s \n " , devpath ) ;
2006-01-23 21:09:17 +03:00
/* get all usb specific information: dev_interface, if_class, dev_usb */
2006-01-09 23:18:00 +03:00
dev = sysfs_device_get ( devpath ) ;
if ( dev = = NULL ) {
err ( " unable to access '%s' " , devpath ) ;
2005-07-04 19:02:25 +04:00
return 1 ;
}
/* usb interface directory */
2006-01-23 21:09:17 +03:00
dev_interface = sysfs_device_get_parent_with_subsystem ( dev , " usb " ) ;
2006-01-09 23:18:00 +03:00
if ( dev_interface = = NULL ) {
2006-01-23 21:09:17 +03:00
info ( " unable to access usb_interface device of '%s' " , devpath ) ;
2005-07-04 19:02:25 +04:00
return 1 ;
}
2005-07-19 22:36:34 +04:00
2006-01-09 23:18:00 +03:00
if_class = sysfs_attr_get_value ( dev_interface - > devpath , " bInterfaceClass " ) ;
2005-07-04 19:02:25 +04:00
if ( ! if_class ) {
2006-08-19 18:06:25 +04:00
info ( " %s: cannot get bInterfaceClass attribute " , dev_interface - > kernel ) ;
2005-07-08 05:29:19 +04:00
return 1 ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
if_class_num = strtoul ( if_class , NULL , 16 ) ;
2006-01-23 21:09:17 +03:00
if ( if_class_num = = 8 ) {
2006-01-09 23:18:00 +03:00
if_subclass = sysfs_attr_get_value ( dev_interface - > devpath , " bInterfaceSubClass " ) ;
2006-01-23 21:09:17 +03:00
if ( if_subclass ! = NULL )
protocol = set_usb_mass_storage_ifsubtype ( type_str , if_subclass , sizeof ( type_str ) - 1 ) ;
} else
set_usb_iftype ( type_str , if_class_num , sizeof ( type_str ) - 1 ) ;
info ( " %s: if_class %d protocol %d \n " , dev_interface - > devpath , if_class_num , protocol ) ;
/* usb device directory */
dev_usb = sysfs_device_get_parent_with_subsystem ( dev_interface , " usb " ) ;
if ( ! dev_usb ) {
info ( " unable to find parent 'usb' device of '%s' " , devpath ) ;
return 1 ;
2005-07-04 19:02:25 +04:00
}
2006-01-23 21:09:17 +03:00
/* mass storage */
if ( protocol = = 6 & & ! use_usb_info ) {
struct sysfs_device * dev_scsi ;
2007-03-21 15:55:48 +03:00
int host , bus , target , lun ;
2006-01-23 21:09:17 +03:00
/* get scsi device */
dev_scsi = sysfs_device_get_parent_with_subsystem ( dev , " scsi " ) ;
if ( dev_scsi = = NULL ) {
info ( " unable to find parent 'scsi' device of '%s' " , devpath ) ;
goto fallback ;
}
2007-03-21 15:55:48 +03:00
if ( sscanf ( dev_scsi - > kernel , " %d:%d:%d:%d " , & host , & bus , & target , & lun ) ! = 4 ) {
info ( " invalid scsi device '%s' " , dev_scsi - > kernel ) ;
goto fallback ;
}
2006-01-23 21:09:17 +03:00
2005-07-04 19:02:25 +04:00
/* Generic SPC-2 device */
2006-01-09 23:18:00 +03:00
scsi_vendor = sysfs_attr_get_value ( dev_scsi - > devpath , " vendor " ) ;
2005-07-04 19:02:25 +04:00
if ( ! scsi_vendor ) {
2006-08-19 18:06:25 +04:00
info ( " %s: cannot get SCSI vendor attribute " , dev_scsi - > kernel ) ;
2006-01-23 21:09:17 +03:00
goto fallback ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
set_str ( vendor_str , scsi_vendor , sizeof ( vendor_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
2006-01-09 23:18:00 +03:00
scsi_model = sysfs_attr_get_value ( dev_scsi - > devpath , " model " ) ;
2005-07-04 19:02:25 +04:00
if ( ! scsi_model ) {
2006-08-19 18:06:25 +04:00
info ( " %s: cannot get SCSI model attribute " , dev_scsi - > kernel ) ;
2006-01-23 21:09:17 +03:00
goto fallback ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
set_str ( model_str , scsi_model , sizeof ( model_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
2006-01-09 23:18:00 +03:00
scsi_type = sysfs_attr_get_value ( dev_scsi - > devpath , " type " ) ;
2005-07-04 19:02:25 +04:00
if ( ! scsi_type ) {
2006-08-19 18:06:25 +04:00
info ( " %s: cannot get SCSI type attribute " , dev_scsi - > kernel ) ;
2006-01-23 21:09:17 +03:00
goto fallback ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
set_scsi_type ( type_str , scsi_type , sizeof ( type_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
2006-01-09 23:18:00 +03:00
scsi_rev = sysfs_attr_get_value ( dev_scsi - > devpath , " rev " ) ;
2005-07-04 19:02:25 +04:00
if ( ! scsi_rev ) {
2006-08-19 18:06:25 +04:00
info ( " %s: cannot get SCSI revision attribute " , dev_scsi - > kernel ) ;
2006-01-23 21:09:17 +03:00
goto fallback ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
set_str ( revision_str , scsi_rev , sizeof ( revision_str ) - 1 ) ;
2007-03-21 15:55:48 +03:00
/*
* some broken devices have the same identifiers
* for all luns , export the target : lun number
*/
sprintf ( instance_str , " %d:%d " , target , lun ) ;
2005-07-04 19:02:25 +04:00
}
2006-01-23 21:09:17 +03:00
fallback :
2005-07-04 19:02:25 +04:00
/* Fallback to USB vendor & device */
if ( vendor_str [ 0 ] = = ' \0 ' ) {
2005-07-08 05:29:19 +04:00
if ( ! use_num_info )
2006-01-09 23:18:00 +03:00
if ( ! ( usb_vendor = sysfs_attr_get_value ( dev_usb - > devpath , " manufacturer " ) ) )
2005-07-08 05:29:19 +04:00
dbg ( " No USB vendor string found, using idVendor " ) ;
2005-07-04 19:02:25 +04:00
if ( ! usb_vendor ) {
2006-01-09 23:18:00 +03:00
if ( ! ( usb_vendor = sysfs_attr_get_value ( dev_usb - > devpath , " idVendor " ) ) ) {
2005-07-08 05:29:19 +04:00
dbg ( " No USB vendor information available \n " ) ;
sprintf ( vendor_str , " 0000 " ) ;
}
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
set_str ( vendor_str , usb_vendor , sizeof ( vendor_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
}
if ( model_str [ 0 ] = = ' \0 ' ) {
2005-07-08 05:29:19 +04:00
if ( ! use_num_info )
2006-01-09 23:18:00 +03:00
if ( ! ( usb_model = sysfs_attr_get_value ( dev_usb - > devpath , " product " ) ) )
2005-07-08 05:29:19 +04:00
dbg ( " No USB model string found, using idProduct " ) ;
2005-07-04 19:02:25 +04:00
if ( ! usb_model ) {
2006-01-09 23:18:00 +03:00
if ( ! ( usb_model = sysfs_attr_get_value ( dev_usb - > devpath , " idProduct " ) ) )
dbg ( " No USB model information available \n " ) ; sprintf ( model_str , " 0000 " ) ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
set_str ( model_str , usb_model , sizeof ( model_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
}
if ( revision_str [ 0 ] = = ' \0 ' ) {
2006-01-09 23:18:00 +03:00
usb_rev = sysfs_attr_get_value ( dev_usb - > devpath , " bcdDevice " ) ;
if ( usb_rev )
set_str ( revision_str , usb_rev , sizeof ( revision_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
}
if ( serial_str [ 0 ] = = ' \0 ' ) {
2006-01-09 23:18:00 +03:00
usb_serial = sysfs_attr_get_value ( dev_usb - > devpath , " serial " ) ;
if ( usb_serial )
set_str ( serial_str , usb_serial , sizeof ( serial_str ) - 1 ) ;
2005-07-04 19:02:25 +04:00
}
return 0 ;
}
int main ( int argc , char * * argv )
{
2006-01-09 23:18:00 +03:00
int retval = 0 ;
const char * env ;
2006-01-18 06:24:48 +03:00
char devpath [ MAX_PATH_LEN ] ;
2007-03-21 15:55:48 +03:00
static int export ;
2005-07-04 19:02:25 +04:00
2006-01-09 23:18:00 +03:00
logging_init ( " usb_id " ) ;
sysfs_init ( ) ;
2007-03-21 15:55:48 +03:00
static const struct option options [ ] = {
{ " usb-info " , 0 , NULL , ' u ' } ,
{ " num-info " , 0 , NULL , ' n ' } ,
{ " export " , 0 , NULL , ' x ' } ,
{ " help " , 0 , NULL , ' h ' } ,
{ }
} ;
2006-01-09 23:18:00 +03:00
2007-03-21 15:55:48 +03:00
while ( 1 ) {
int option ;
option = getopt_long ( argc , argv , " nuxh " , options , NULL ) ;
if ( option = = - 1 )
break ;
2005-07-04 19:02:25 +04:00
switch ( option ) {
2005-07-08 05:29:19 +04:00
case ' n ' :
2006-01-09 23:18:00 +03:00
use_num_info = 1 ;
use_usb_info = 1 ;
2005-07-08 05:29:19 +04:00
break ;
2005-07-04 19:02:25 +04:00
case ' u ' :
2006-01-09 23:18:00 +03:00
use_usb_info = 1 ;
2005-07-04 19:02:25 +04:00
break ;
case ' x ' :
2006-01-09 23:18:00 +03:00
export = 1 ;
2005-07-04 19:02:25 +04:00
break ;
2007-03-21 15:55:48 +03:00
case ' h ' :
printf ( " Usage: usb_id [--usb-info] [--num-info] [--export] [--help] \n "
" --usb-info use usb strings instead \n "
" --num-info use numerical values \n "
" --export print values as environemt keys \n "
" --help print this help text \n \n " ) ;
2005-07-04 19:02:25 +04:00
default :
2005-07-08 05:29:19 +04:00
retval = 1 ;
2007-03-21 15:55:48 +03:00
goto exit ;
2005-07-04 19:02:25 +04:00
}
}
2006-01-09 23:18:00 +03:00
env = getenv ( " DEVPATH " ) ;
if ( env ! = NULL )
strlcpy ( devpath , env , sizeof ( devpath ) ) ;
else {
2007-03-21 15:55:48 +03:00
if ( argv [ optind ] = = NULL ) {
2005-07-04 19:02:25 +04:00
fprintf ( stderr , " No device specified \n " ) ;
2006-01-09 23:18:00 +03:00
retval = 1 ;
goto exit ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
strlcpy ( devpath , argv [ optind ] , sizeof ( devpath ) ) ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
retval = usb_id ( devpath ) ;
2005-07-04 19:02:25 +04:00
2005-07-08 05:29:19 +04:00
if ( retval = = 0 ) {
2007-03-21 15:55:48 +03:00
char serial [ 256 ] ;
strlcpy ( serial , vendor_str , sizeof ( serial ) ) ;
strlcat ( serial , " _ " , sizeof ( serial ) ) ;
strlcat ( serial , model_str , sizeof ( serial ) ) ;
if ( serial_str [ 0 ] ! = ' \0 ' ) {
strlcat ( serial , " _ " , sizeof ( serial ) ) ;
strlcat ( serial , serial_str , sizeof ( serial ) ) ;
}
if ( instance_str [ 0 ] ! = ' \0 ' ) {
strlcat ( serial , " - " , sizeof ( serial ) ) ;
strlcat ( serial , instance_str , sizeof ( serial ) ) ;
}
2005-07-08 05:29:19 +04:00
if ( export ) {
printf ( " ID_VENDOR=%s \n " , vendor_str ) ;
printf ( " ID_MODEL=%s \n " , model_str ) ;
printf ( " ID_REVISION=%s \n " , revision_str ) ;
2007-03-21 15:55:48 +03:00
printf ( " ID_SERIAL=%s \n " , serial ) ;
if ( serial_str [ 0 ] ! = ' \0 ' )
printf ( " ID_SERIAL_SHORT=%s \n " , serial_str ) ;
2005-07-08 05:29:19 +04:00
printf ( " ID_TYPE=%s \n " , type_str ) ;
2007-03-21 15:55:48 +03:00
if ( instance_str [ 0 ] ! = ' \0 ' )
printf ( " ID_INSTANCE=%s \n " , instance_str ) ;
2005-07-19 19:18:19 +04:00
printf ( " ID_BUS=usb \n " ) ;
2007-03-21 15:55:48 +03:00
} else
printf ( " %s \n " , serial ) ;
2005-07-04 19:02:25 +04:00
}
2006-01-09 23:18:00 +03:00
exit :
sysfs_cleanup ( ) ;
logging_close ( ) ;
return retval ;
2005-07-04 19:02:25 +04:00
}