1# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# # Use of this source code is governed by a BSD-style license that can be
3# # found in the LICENSE file.
4#
5# """extract data from output of use-devices on linux box"""
6# The parser takes output of "usb-devices" as rawdata, and has capablities to
7# 1. Populate usb data into dictionary
8# 3. Extract defined peripheral devices based on CAMERA_LIST, SPEAKER_LIST.
9# 4. As of now only one type touch panel is defined here, which is Mimo.
10# 5. Check usb devices's interface.
11# 6. Retrieve usb device based on product and manufacture.
12#
13import cStringIO
14from autotest_lib.client.common_lib.cros import textfsm
15
16USB_DEVICES_TPLT = (
17    'Value Required Vendor ([0-9a-fA-F]+)\n'
18    'Value Required ProdID ([0-9A-Fa-f]+)\n'
19    'Value Required prev ([0-9a-fA-Z.]+)\n'
20    'Value Manufacturer (.+)\n'
21    'Value Product (.+)\n'
22    'Value serialnumber ([0-9a-fA-Z\:\-]+)\n'
23    'Value cinterfaces (\d)\n'
24    'Value List intindex ([0-9])\n'
25    'Value List intdriver ([A-Za-z-\(\)]+)\n\n'
26    'Start\n'
27         '  ^USB-Device -> Continue.Record\n'
28         '  ^P:\s+Vendor=${Vendor}\s+ProdID=${ProdID}\sRev=${prev}\n'
29         '  ^S:\s+Manufacturer=${Manufacturer}\n'
30         '  ^S:\s+Product=${Product}\n'
31         '  ^S:\s+SerialNumber=${serialnumber}\n'
32         '  ^C:\s+\#Ifs=\s+${cinterfaces}\n'
33         '  ^I:\s+If\#=\s+${intindex}.*Driver=${intdriver}\n'
34)
35
36# As of now there are certain types of cameras, speakers and touch-panel.
37# New devices can be added to these global variables.
38CAMERA_LIST = ['2bd9:0011', '046d:0843', '046d:082d', '046d:0853', '064e:9405',
39               '046d:085f']
40CAMERA_MAP = {'2bd9:0011':'Huddly GO',
41              '046d:0843':'Logitech Webcam C930e',
42              '046d:082d':'HD Pro Webcam C920',
43              '046d:0853':'PTZ Pro Camera',
44              '046d:085f':'PTZ Pro 2',
45              '064e:9405':'HD WebCam'}
46
47SPEAKER_LIST = ['18d1:8001', '0b0e:0412', '2abf:0505']
48SPEAKER_MAP = {'18d1:8001':'Hangouts Meet speakermic',
49               '0b0e:0412':'Jabra SPEAK 410',
50               '2abf:0505':'FLX UC 500'}
51
52TOUCH_DISPLAY_LIST = ['17e9:016b','17e9:416d']
53TOUCH_CONTROLLER_LIST = ['266e:0110']
54
55DISPLAY_PANEL_MAP = {'17e9:016b':'DisplayLink',
56                     '17e9:416d':'DisplayLink'}
57
58TOUCH_PANEL_MAP = {'266e:0110':'SiS HID Touch Controller'}
59
60
61INTERFACES_LIST = {'2bd9:0011':['uvcvideo', 'uvcvideo',
62                                'uvcvideo', 'uvcvideo'],
63                   '046d:0843':['uvcvideo', 'uvcvideo',
64                                'snd-usb-audio', 'snd-usb-audio'],
65                   '046d:082d':['uvcvideo', 'uvcvideo',
66                                'snd-usb-audio', 'snd-usb-audio'],
67                   '046d:085f': ['uvcvideo', 'uvcvideo','usbhid'],
68                   '0b0e:0412':['snd-usb-audio', 'snd-usb-audio',
69                                'snd-usb-audio'],
70                   '18d1:8001':['snd-usb-audio', 'snd-usb-audio',
71                                'snd-usb-audio', 'usbhid'],
72                   '17e9:016b':['udl'],
73                   '17e9:416d':['udl'],
74                   '266e:0110':['usbhid'],
75                   '046d:0853':['uvcvideo', 'uvcvideo','usbhid'],
76                   '064e:9405':['uvcvideo', 'uvcvideo'],
77                   '2abf:0505':['snd-usb-audio', 'snd-usb-audio',
78                                'snd-usb-audio', 'usbhid']
79                  }
80
81
82def extract_usb_data(rawdata):
83    """populate usb data into list dictionary
84    @param rawdata: The output of "usb-devices" on CfM.
85    @returns list of dictionary, examples:
86    {'Manufacturer': 'USBest Technology', 'Product': 'SiS HID Touch Controller',
87     'Vendor': '266e', 'intindex': ['0'], 'tport': '00', 'tcnt': '01',
88     'serialnumber': '', 'tlev': '03', 'tdev': '18', 'dver': '',
89     'intdriver': ['usbhid'], 'tbus': '01', 'prev': '03.00',
90     'cinterfaces': '1', 'ProdID': '0110', 'tprnt': '14'}
91    """
92    usbdata = []
93    rawdata += '\n'
94    re_table = textfsm.TextFSM(cStringIO.StringIO(USB_DEVICES_TPLT))
95    fsm_results = re_table.ParseText(rawdata)
96    usbdata = [dict(zip(re_table.header, row)) for row in fsm_results]
97    return usbdata
98
99
100def extract_peri_device(usbdata, vid_pid):
101    """retrive the list of dictionary for certain types of VID_PID
102    @param usbdata:  list of dictionary for usb devices
103    @param vid_pid: list of vid_pid combination
104    @returns the list of dictionary for certain types of VID_PID
105    """
106    vid_pid_usb_list = []
107    for _vid_pid in vid_pid:
108        vid = _vid_pid.split(':')[0]
109        pid = _vid_pid.split(':')[1]
110        for _data in usbdata:
111            if vid == _data['Vendor']  and pid ==  _data['ProdID']:
112                vid_pid_usb_list.append(_data)
113    return  vid_pid_usb_list
114
115
116def get_list_audio_device(usbdata):
117    """retrive the list of dictionary for all audio devices
118    @param usbdata:  list of dictionary for usb devices
119    @returns the list of dictionary for all audio devices
120    """
121    audio_device_list = []
122    for _data in usbdata:
123        if "snd-usb-audio" in _data['intdriver']:
124           audio_device_list.append(_data)
125    return audio_device_list
126
127
128def get_list_video_device(usbdata):
129    """retrive the list of dictionary for all video devices
130    @param usbdata:  list of dictionary for usb devices
131    @returns the list of dictionary for all video devices
132    """
133    video_device_list = []
134    for _data in usbdata:
135        if "uvcvideo" in _data['intdriver']:
136             video_device_list.append(_data)
137    return video_device_list
138
139
140def get_list_mimo_device(usbdata):
141    """retrive the list of dictionary for all touch panel devices
142    @param usbdata:  list of dictionary for usb devices
143    @returns the lists of dictionary
144             one for displaylink, the other for touch controller
145    """
146    displaylink_list = []
147    touchcontroller_list = []
148    for _data in usbdata:
149        if "udl" in _data['intdriver']:
150            displaylink_list.append(_data)
151        if "SiS HID Touch Controller" == _data['Product']:
152            touchcontroller_list.append(_data)
153    return displaylink_list, touchcontroller_list
154
155
156def get_list_by_product(usbdata, product_name):
157    """retrive the list of dictionary based on product_name
158    @param usbdata:  list of dictionary for usb devices
159    @returns the list of dictionary
160    """
161    usb_list_by_product = []
162    for _data in usbdata:
163        if product_name == _data['Product']:
164            usb_list_by_product.append(_data)
165    return usb_list_by_product
166
167
168def get_list_by_manufacturer(usbdata, manufacturer_name):
169    """retrive the list of dictionary based on manufacturer_name
170    @param usbdata:  list of dictionary for usb devices
171    @returns the list of dictionary
172    """
173    usb_list_by_manufacturer = []
174    for _data in usbdata:
175        if manufacturer_name == _data['Manufacturer']:
176            usb_list_by_manufacturer.append(_data)
177    return usb_list_by_manufacturer
178
179
180def is_usb_device_ok(usbdata, vid_pid):
181    """check usb device has expected usb interface
182    @param usbdata:  list of dictionary for usb devices
183    @vid_pid: VID, PID combination for each type of USB device
184    @returns:
185              int: number of device
186              boolean: usb interfaces expected or not?
187    """
188    number_of_device = 0
189    device_health = []
190    vid = vid_pid[0:4]
191    pid = vid_pid[-4:]
192    for _data in usbdata:
193        if vid == _data['Vendor']  and pid ==  _data['ProdID']:
194            number_of_device += 1
195            compare_list = _data['intdriver'][0:len(INTERFACES_LIST[vid_pid])]
196            if  cmp(compare_list, INTERFACES_LIST[vid_pid]) == 0:
197                device_health.append('1')
198            else:
199                device_health.append('0')
200    return number_of_device, device_health
201
202
203def get_speakers(usbdata):
204    """get number of speaker for each type
205    @param usbdata:  list of dictionary for usb devices
206    @returns: list of dictionary, key is VID_PID, value is number of speakers
207    """
208    number_speaker = {}
209    for _speaker in SPEAKER_LIST:
210        vid =  _speaker.split(':')[0]
211        pid =  _speaker.split(':')[1]
212        _number = 0
213        for _data in usbdata:
214            if _data['Vendor'] == vid and _data['ProdID'] == pid:
215                _number += 1
216        number_speaker[_speaker] = _number
217    return number_speaker
218
219
220def get_dual_speaker(usbdata):
221    """check whether dual speakers are present
222    @param usbdata:  list of dictionary for usb devices
223    @returns: True or False
224    """
225    dual_speaker = None
226    speaker_dict = get_speakers(usbdata)
227    for _key in speaker_dict.keys():
228        if speaker_dict[_key] == 2:
229            dual_speaker = _key
230            break
231    return dual_speaker
232
233
234def get_cameras(usbdata):
235    """get number of camera for each type
236    @param usbdata:  list of dictionary for usb devices
237    @returns: list of dictionary, key is VID_PID, value is number of cameras
238    """
239    number_camera = {}
240    for _camera in CAMERA_LIST:
241        vid =  _camera.split(':')[0]
242        pid =  _camera.split(':')[1]
243        _number = 0
244        for _data in usbdata:
245            if _data['Vendor'] == vid and  _data['ProdID'] == pid:
246                _number += 1
247        number_camera[_camera] = _number
248    return number_camera
249
250def get_display_mimo(usbdata):
251    """get number of displaylink in Mimo for each type
252    @param usbdata:  list of dictionary for usb devices
253    @returns: list of dictionary, key is VID_PID, value
254              is number of displaylink
255    """
256    number_display = {}
257    for _display in TOUCH_DISPLAY_LIST:
258        vid =  _display.split(':')[0]
259        pid =  _display.split(':')[1]
260        _number = 0
261        for _data in usbdata:
262            if _data['Vendor'] == vid and  _data['ProdID'] == pid:
263                _number += 1
264        number_display[_display] = _number
265    return number_display
266
267def get_controller_mimo(usbdata):
268    """get number of touch controller Mimo for each type
269    @param usbdata:  list of dictionary for usb devices
270    @returns: list of dictionary, key is VID_PID, value
271              is number of touch controller
272    """
273    number_controller = {}
274    for _controller in TOUCH_CONTROLLER_LIST:
275        vid =  _controller.split(':')[0]
276        pid =  _controller.split(':')[1]
277        _number = 0
278        for _data in usbdata:
279            if _data['Vendor'] == vid and  _data['ProdID'] == pid:
280                _number += 1
281        number_controller[_controller] = _number
282    return number_controller
283
284def get_preferred_speaker(peripheral):
285    """get string for the 1st speakers in the device list
286     @param peripheral:  of dictionary for usb devices
287     @returns: string for name of preferred speake
288    """
289    for _key in peripheral:
290        if _key in SPEAKER_LIST:
291            speaker_name = SPEAKER_MAP[_key]+' ('+_key+')'
292            return speaker_name
293
294def get_preferred_camera(peripheral):
295    """get string for the 1st cameras in the device list
296    @param peripheral:  of dictionary for usb devices
297    @returns: string for name of preferred camera
298    """
299    for _key in peripheral:
300        if _key in CAMERA_LIST:
301            camera_name = CAMERA_MAP[_key]+' ('+_key+')'
302            return camera_name
303
304def get_device_prod(vid_pid):
305    """get product for vid_pid
306    @param vid_pid: vid and pid combo for device
307    @returns: product
308    """
309    for _key in SPEAKER_MAP.keys():
310        if _key == vid_pid:
311            return SPEAKER_MAP[_key]
312    for _key in CAMERA_MAP.keys():
313        if _key == vid_pid:
314            return CAMERA_MAP[_key]
315    for _key in DISPLAY_PANEL_MAP.keys():
316        if _key == vid_pid:
317            return DISPLAY_PANEL_MAP[_key]
318    for _key in TOUCH_PANEL_MAP.keys():
319        if _key == vid_pid:
320            return TOUCH_PANEL_MAP[_key]
321    return None
322