read_device/
read_device.rs

1extern crate libusb_sys as ffi;
2extern crate libc;
3
4use libc::{c_int,c_uint,c_uchar};
5
6use std::mem;
7use std::slice;
8
9use std::io::{Read,Cursor};
10use std::str::FromStr;
11
12#[derive(Debug)]
13struct Endpoint {
14  config: u8,
15  iface: u8,
16  setting: u8,
17  address: u8
18}
19
20fn main() {
21  let args: Vec<String> = std::env::args().collect();
22
23  if args.len() < 3 {
24    println!("usage: show_device <vendor-id> <product-id>");
25    return;
26  }
27
28  let vid: u16 = FromStr::from_str(args[1].as_ref()).unwrap();
29  let pid: u16 = FromStr::from_str(args[2].as_ref()).unwrap();
30
31  let mut context: *mut ::ffi::libusb_context = unsafe { mem::uninitialized() };
32  let mut device_list: *const *mut ::ffi::libusb_device = unsafe { mem::uninitialized() };
33
34  match unsafe { ::ffi::libusb_init(&mut context) } {
35    0 => (),
36    e => panic!("libusb_init: {}", e)
37  };
38
39  let handle = unsafe { ::ffi::libusb_open_device_with_vid_pid(context, vid, pid) };
40
41  if !handle.is_null() {
42    match unsafe { ::ffi::libusb_reset_device(handle) } {
43      0 => {
44        unsafe { ::ffi::libusb_set_auto_detach_kernel_driver(handle, 0) };
45
46        let device = unsafe { ::ffi::libusb_get_device(handle) };
47        unsafe { ::ffi::libusb_ref_device(device) };
48
49        if unsafe { ::ffi::libusb_get_device_list(context, &mut device_list) } >= 0 {
50          print_device_tree(device);
51          println!("");
52
53          unsafe { ::ffi::libusb_free_device_list(device_list, 1) };
54        }
55
56        let languages = get_language_ids(handle);
57        println!("Supported languages: {:?}", languages);
58
59        let mut active_config: c_int = unsafe { mem::uninitialized() };
60        match unsafe { ::ffi::libusb_get_configuration(handle, &mut active_config) } {
61          0 => println!("Active configuration: {}", active_config),
62          e => println!("libusb_get_configuration: {}", e)
63        }
64        println!("");
65
66        match find_readable_endpoint(device, ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT) {
67          Some(ep) => read_endpoint(handle, device, ep, ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT),
68          None => println!("No readable interrupt endpoint")
69        }
70        println!("");
71
72        match find_readable_endpoint(device, ::ffi::LIBUSB_TRANSFER_TYPE_BULK) {
73          Some(ep) => read_endpoint(handle, device, ep, ::ffi::LIBUSB_TRANSFER_TYPE_BULK),
74          None => println!("No readable bulk endpoint")
75        }
76
77        unsafe { ::ffi::libusb_unref_device(device) };
78      },
79      e => println!("libusb_reset_device: {}", e)
80    }
81
82    unsafe { ::ffi::libusb_close(handle) };
83  }
84
85  unsafe { ::ffi::libusb_exit(context) };
86}
87
88fn print_device_tree(device: *mut ::ffi::libusb_device) -> usize {
89  if device.is_null() {
90    return 0;
91  }
92
93  let parent = unsafe { ::ffi::libusb_get_parent(device) };
94  let depth = print_device_tree(parent);
95
96  for _ in 0..depth {
97    print!("  ");
98  }
99
100  let bus = unsafe { ::ffi::libusb_get_bus_number(device) };
101  let address = unsafe { ::ffi::libusb_get_device_address(device) };
102
103  println!("Bus {:03} Device {:03}", bus, address);
104
105  return depth + 1;
106}
107
108fn get_language_ids(handle: *mut ::ffi::libusb_device_handle) -> Vec<u16> {
109  let mut buf = Vec::<u8>::with_capacity(255);
110  let len = unsafe { ::ffi::libusb_get_string_descriptor(handle, 0, 0, (&mut buf[..]).as_mut_ptr() as *mut c_uchar, buf.capacity() as c_int) };
111
112  let mut languages = Vec::<u16>::new();
113
114  if len >= 0 {
115    unsafe { buf.set_len(len as usize) };
116
117    if buf.len() >= 2 {
118      let num_languages = (buf.len() - 2) / 2;
119      languages.reserve(num_languages);
120
121      let mut cursor = Cursor::new(buf);
122      cursor.set_position(2);
123
124      for _ in 0..num_languages {
125        let mut bytes = Vec::<u8>::with_capacity(2);
126
127        match cursor.read(unsafe { slice::from_raw_parts_mut((&mut bytes[..]).as_mut_ptr(), bytes.capacity()) }) {
128          Ok(len) => {
129            if len == 2 {
130              unsafe { bytes.set_len(len) };
131
132              let langid = (bytes[1] as u16) << 8 | (bytes[0] as u16);
133              languages.push(langid)
134            }
135            else {
136              return languages;
137            }
138          },
139          Err(_) => return languages
140        }
141      }
142    }
143  }
144  else {
145    println!("libusb_get_string_descriptor: {}", len);
146  }
147
148  languages
149}
150
151fn find_readable_endpoint(device: *mut ::ffi::libusb_device, transfer_type: u8) -> Option<Endpoint> {
152  let mut device_descriptor: ::ffi::libusb_device_descriptor = unsafe { mem::uninitialized() };
153
154  match unsafe { ::ffi::libusb_get_device_descriptor(device, &mut device_descriptor) } {
155    0 => {
156      for i in 0..device_descriptor.bNumConfigurations {
157        let mut config_ptr: *const ::ffi::libusb_config_descriptor = unsafe { mem::uninitialized() };
158
159        match unsafe { ::ffi::libusb_get_config_descriptor(device, i, &mut config_ptr) } {
160          0 => {
161            let config_descriptor = unsafe { &*config_ptr };
162            let interfaces = unsafe { slice::from_raw_parts(config_descriptor.interface, config_descriptor.bNumInterfaces as usize) };
163
164            for iface in interfaces {
165              let settings = unsafe { slice::from_raw_parts(iface.altsetting, iface.num_altsetting as usize) };
166
167              for iface_descriptor in settings {
168                let endpoints = unsafe { slice::from_raw_parts(iface_descriptor.endpoint, iface_descriptor.bNumEndpoints as usize) };
169
170                for endpoint_descriptor in endpoints {
171                  let is_input = endpoint_descriptor.bEndpointAddress & ::ffi::LIBUSB_ENDPOINT_DIR_MASK == ::ffi::LIBUSB_ENDPOINT_IN;
172                  let matches_type = endpoint_descriptor.bmAttributes & ::ffi::LIBUSB_TRANSFER_TYPE_MASK == transfer_type;
173
174                  if is_input && matches_type {
175                    return Some(Endpoint {
176                      config: config_descriptor.bConfigurationValue,
177                      iface: iface_descriptor.bInterfaceNumber,
178                      setting: iface_descriptor.bAlternateSetting,
179                      address: endpoint_descriptor.bEndpointAddress
180                    });
181                  }
182                }
183              }
184            }
185          },
186          e => println!("libusb_get_config_descriptor: {}", e)
187        }
188      }
189
190      None
191    },
192    e => {
193      println!("libusb_get_device_descriptor: {}", e);
194      None
195    }
196  }
197}
198
199fn read_endpoint(handle: *mut ::ffi::libusb_device_handle, device: *mut ::ffi::libusb_device, endpoint: Endpoint, transfer_type: u8) {
200  println!("Reading from endpoint: {:?}", endpoint);
201
202  let has_kernel_driver = unsafe {
203    if ::ffi::libusb_kernel_driver_active(handle, endpoint.iface as c_int) == 1 {
204      match ::ffi::libusb_detach_kernel_driver(handle, endpoint.iface as c_int) {
205        0 => (),
206        e => println!("libusb_detach_kernel_driver: {}", e)
207      }
208
209      true
210    }
211    else {
212      false
213    }
214  };
215
216  println!(" - kernel driver? {}", has_kernel_driver);
217
218  match unsafe { ::ffi::libusb_set_configuration(handle, endpoint.config as c_int) } {
219    0 => {
220      println!(" - max packet size: {}", unsafe { ::ffi::libusb_get_max_packet_size(device, endpoint.address as c_uchar) });
221      println!(" - max iso packet size: {}", unsafe { ::ffi::libusb_get_max_iso_packet_size(device, endpoint.address as c_uchar) });
222
223      match unsafe { ::ffi::libusb_claim_interface(handle, endpoint.iface as c_int) } {
224        0 => {
225          match unsafe { ::ffi::libusb_set_interface_alt_setting(handle, endpoint.iface as c_int, endpoint.setting as c_int) } {
226            0 => {
227              let mut vec = Vec::<u8>::with_capacity(256);
228              let timeout: c_uint = 1000;
229
230              let mut transferred: c_int = unsafe { mem::uninitialized() };
231
232              match transfer_type {
233                ::ffi::LIBUSB_TRANSFER_TYPE_INTERRUPT => {
234                  match unsafe { ::ffi::libusb_interrupt_transfer(handle, endpoint.address as c_uchar, (&vec[..]).as_ptr() as *mut c_uchar, vec.capacity() as c_int, &mut transferred, timeout) } {
235                    0 => {
236                      unsafe { vec.set_len(transferred as usize) };
237                      println!(" - read: {:?}", vec);
238                    },
239                    e => println!("libusb_interrupt_transfer: {}", e)
240                  }
241                },
242                ::ffi::LIBUSB_TRANSFER_TYPE_BULK => {
243                  match unsafe { ::ffi::libusb_bulk_transfer(handle, endpoint.address as c_uchar, (&vec[..]).as_ptr() as *mut c_uchar, vec.capacity() as c_int, &mut transferred, timeout) } {
244                    0 => {
245                      unsafe { vec.set_len(transferred as usize) };
246                      println!(" - read: {:?}", vec);
247                    },
248                    e => println!("libusb_interrupt_transfer: {}", e)
249                  }
250                },
251                tt => println!(" - can't read endpoint with transfer type {}", tt)
252              }
253            },
254            e => println!("libusb_set_interface_alt_setting: {}", e)
255          }
256
257          match unsafe { ::ffi::libusb_release_interface(handle, endpoint.iface as c_int) } {
258            0 => (),
259            e => println!("libusb_release_interface: {}", e)
260          }
261        },
262        e => println!("libusb_claim_interface: {}", e)
263      }
264    },
265    e => println!("libusb_set_configuration: {}", e)
266  }
267
268
269
270  if has_kernel_driver {
271    match unsafe { ::ffi::libusb_attach_kernel_driver(handle, endpoint.iface as c_int) } {
272      0 => (),
273      e => println!("libusb_attach_kernel_driver: {}", e)
274    }
275  }
276}