~albertomilone/envy/envyng-core

« back to all changes in this revision

Viewing changes to Envy/detection.py

  • Committer: albertomilone at alice
  • Date: 2008-09-30 10:29:41 UTC
  • Revision ID: albertomilone@alice.it-20080930102941-hv00uqyztdfsgx71
releaseĀ 2.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os
 
2
import subprocess
 
3
from subprocess import PIPE, Popen
 
4
import re
 
5
import sys
 
6
 
 
7
class HardwareDetection(object):
 
8
    '''
 
9
    A simple class to:
 
10
      * Detect the available graphics cards
 
11
      * See what drivers support them (If they are
 
12
        ATI or NVIDIA cards). If more than one card is 
 
13
        available, try to find the highest common 
 
14
        driver version which supports them all.
 
15
        (READ the comments in the code for further
 
16
        details)
 
17
      * Return a dictionary such as the following:
 
18
      
 
19
      
 
20
      {
 
21
      'fglrx': {
 
22
                0: {'compatible': True, 'recommended': True, 'name': 'xorg-driver-fglrx'}
 
23
               },
 
24
      'nvidia': {
 
25
                0: {'compatible': True, 'recommended': False, 'name': 'nvidia-glx-177'},
 
26
                1: {'compatible': True, 'recommended': True, 'name': 'nvidia-glx-173'},
 
27
                2: {'compatible': True, 'recommended': False, 'name': 'nvidia-glx-96'},
 
28
                3: {'compatible': False, 'recommended': False, 'name': 'nvidia-glx-71'}
 
29
                }
 
30
      }
 
31
      
 
32
    '''
 
33
    
 
34
    aliasesPath = '/usr/share/jockey/modaliases/'
 
35
    driverDetails = {}
 
36
    
 
37
    def __init__(self, datadir=aliasesPath):
 
38
        '''
 
39
        If the modaliases path does not exist
 
40
        print none, so that debconf is not triggered.
 
41
        '''
 
42
        if not os.path.isdir(datadir):
 
43
            #print 'none'
 
44
            raise IOError, "dir %s not found" % datadir
 
45
        self.datadir = datadir
 
46
        self.detection()
 
47
        self.getData()
 
48
        self.getCards()
 
49
        self.removeUnsupported()
 
50
        
 
51
    def detection(self):
 
52
        '''
 
53
        Detect the models of the graphics cards
 
54
        and store them in self.cards
 
55
        '''
 
56
        self.cards = []
 
57
        p1 = Popen(['lspci', '-n'], stdout=PIPE)
 
58
        p = p1.communicate()[0].split('\n')
 
59
        indentifier1 = re.compile('.*0300: *(.+):(.+) \(.+\)')
 
60
        indentifier2 = re.compile('.*0300: *(.+):(.+)')
 
61
        for line in p:
 
62
            m1 = indentifier1.match(line)
 
63
            m2 = indentifier2.match(line)
 
64
            if m1:
 
65
                id1 = m1.group(1).strip().lower()
 
66
                id2 = m1.group(2).strip().lower()
 
67
                id = id1 + ':' + id2
 
68
                self.cards.append(id)
 
69
            elif m2:
 
70
                id1 = m2.group(1).strip().lower()
 
71
                id2 = m2.group(2).strip().lower()
 
72
                id = id1 + ':' + id2
 
73
                self.cards.append(id)
 
74
 
 
75
    def getData(self):
 
76
        '''
 
77
        Get the data from the modaliases for each driver
 
78
        and store them in self.drivers
 
79
        '''
 
80
        
 
81
        files = os.listdir(self.datadir)
 
82
        self.drivers = {}
 
83
        
 
84
        for file in files:
 
85
            a = open(self.datadir + file, 'r')
 
86
            lines = a.readlines()
 
87
            NvidiaIdentifier = re.compile('.*alias pci:v0000(.+)d0000(.+)sv.*nvidia.*nvidia-glx-(.+).*')
 
88
            AtiIdentifier = re.compile('.*alias pci:v0000(.+)d0000(.+)sv\*sd\*bc03sc\*i\*.*fglrx.*')
 
89
            for line in lines:
 
90
                m1 = NvidiaIdentifier.match(line)
 
91
                m2 = AtiIdentifier.match(line)
 
92
                if m1:
 
93
                    id1 = m1.group(1).strip().lower()#e.g. 10de or 12d2 for nvidia
 
94
                    id2 = m1.group(2).strip().lower()#the id which is specific to the model
 
95
                    drivername = m1.group(3).strip().lower()#e.g. 173, 177, 96 or 71
 
96
                    if id1 in ['10de', '12d2']:#if NVIDIA
 
97
                        self.drivers.setdefault('nvidia', {}).setdefault(int(drivername), []).append(id1 + ':' + id2)
 
98
                elif m2:
 
99
                    id1 = m2.group(1).strip().lower()#e.g. 1002 for ati
 
100
                    id2 = m2.group(2).strip().lower()#the id which is specific to the model
 
101
                    drivername = 'fglrx'#m1.group(3).strip().lower()#e.g. 173, 177, 96 or 71
 
102
                    if id1 in ['1002']:#if ATI
 
103
                        self.drivers.setdefault('fglrx', {}).setdefault(drivername, []).append(id1 + ':' + id2)
 
104
            a.close()
 
105
        
 
106
        
 
107
        '''
 
108
        If the modaliases files don't contain anything useful
 
109
        just print none and exit so as not to trigger debconf.
 
110
        '''
 
111
        if len(self.drivers.keys()) == 0:
 
112
            raise ValueError, "modaliases have no useful information"
 
113
    
 
114
    def getCards(self):
 
115
        '''
 
116
        See if the detected graphics cards are NVIDIA cards.
 
117
        If they are NVIDIA cards, append them to self.nvidiaCards
 
118
        '''
 
119
        self.driversForCards = {}
 
120
        self.nvidiaCards = []
 
121
        self.atiCards = []
 
122
        '''
 
123
        It is possible to override hardware detection (only for testing
 
124
        purposes) by setting self.cards to one of the following lists:
 
125
        
 
126
        self.cards = ['10de:02e2', '10de:002d', '10de:0296', '10de:087f']
 
127
        self.cards = ['10de:02e2', '10de:087f']
 
128
        self.cards = ['10de:02e2', '10de:087f', '10de:fake']
 
129
        self.cards = ['10de:fake']
 
130
        self.cards = ['1002:fake'] ATI
 
131
        self.cards = ['1002:95c7'] ATI
 
132
        '''
 
133
        #self.cards = ['1002:fake', '1002:95c7']
 
134
        
 
135
        for card in self.cards:
 
136
            if card[0: card.find(':')] in ['10de', '12d2']:
 
137
                self.nvidiaCards.append(card)
 
138
            elif card[0: card.find(':')] == '1002':
 
139
                self.atiCards.append(card)
 
140
        
 
141
        self.nvidiaOrderedList = self.drivers['nvidia'].keys()
 
142
        self.nvidiaOrderedList.sort(reverse=True)
 
143
        
 
144
        self.atiOrderedList = self.drivers['fglrx'].keys()
 
145
        '''
 
146
        See what drivers support each card and fill self.driversForCards
 
147
        so as to have something like the following:
 
148
        
 
149
        self.driversForCards = {
 
150
                                 'id_of_card1': [driver1, driver2],
 
151
                                 'id_of_card2': [driver2, driver3],
 
152
                               }
 
153
        '''
 
154
        for card in self.nvidiaCards:
 
155
            supported = False
 
156
            for driver in self.nvidiaOrderedList:
 
157
                if card in self.drivers['nvidia'][driver]:
 
158
                    supported = True
 
159
                    self.driversForCards.setdefault(card, []).append(driver)
 
160
            if supported == False:
 
161
                self.driversForCards.setdefault(card, []).append(None)
 
162
        
 
163
        for card in self.atiCards:
 
164
            supported = False
 
165
            for driver in self.atiOrderedList:
 
166
                if card in self.drivers['fglrx'][driver]:
 
167
                    supported = True
 
168
                    self.driversForCards.setdefault(card, []).append(driver)
 
169
            if supported == False:
 
170
                self.driversForCards.setdefault(card, []).append(None)
 
171
 
 
172
    def removeUnsupported(self):
 
173
        '''
 
174
        Remove unsupported cards from self.nvidiaCards and from
 
175
        self.driversForCards
 
176
        '''
 
177
        #print self.driversForCards
 
178
        unsupportedCards = []
 
179
        for card in self.driversForCards:
 
180
            if None in self.driversForCards[card]:
 
181
                unsupportedCards.append(card)
 
182
 
 
183
        for unsupported in unsupportedCards:
 
184
            try:
 
185
                self.nvidiaCards.remove(unsupported)
 
186
            except ValueError:
 
187
                self.atiCards.remove(unsupported)
 
188
            del self.driversForCards[unsupported]
 
189
 
 
190
    def selectDriver(self):
 
191
        '''
 
192
        If more than one card is available, try to get the highest common driver
 
193
        '''
 
194
        
 
195
        compatibleDrivers = {}
 
196
        recommendedDrivers = {}
 
197
        availableDrivers = {}
 
198
        
 
199
        nvidiaCardsNumber = len(self.nvidiaCards)
 
200
        atiCardsNumber = len(self.atiCards)
 
201
        if nvidiaCardsNumber > 0:#if a NVIDIA card is available
 
202
            if nvidiaCardsNumber > 1:#if more than 1 card
 
203
                '''
 
204
                occurrence stores the number of occurrences (the values of the
 
205
                dictionary) of each driver version (the keys of the dictionary)
 
206
                
 
207
                Example:
 
208
                    occurrence = {177: 1, 173: 3}
 
209
                    This means that driver 177 supports only 1 card while 173
 
210
                    supports 3 cards.
 
211
                '''
 
212
                occurrence = {}
 
213
                #print self.driversForCards
 
214
                for card in self.driversForCards:
 
215
                    if '1002' not in card:
 
216
                        for drv in self.driversForCards[card]:
 
217
                            occurrence.setdefault(drv, 0)
 
218
                            occurrence[drv] += 1
 
219
                
 
220
                occurrences = occurrence.keys()
 
221
                occurrences.sort(reverse=True)
 
222
                '''
 
223
                candidates is the list of the likely candidates for the
 
224
                installation
 
225
                '''
 
226
                candidates = []
 
227
                for driver in occurrences:
 
228
                    if occurrence[driver] == nvidiaCardsNumber:
 
229
                        candidates.append(driver)
 
230
                
 
231
                
 
232
                if len(candidates) > 0:
 
233
                    '''
 
234
                    If more than one driver version works for all the available
 
235
                    cards then the newest one is selected.
 
236
                    
 
237
                    USE-CASE:
 
238
                        If a user has the following cards:
 
239
                        * GeForce 9300 (supported by driver 177 and 173)
 
240
                        * GeForce 7300 (supported by driver 177 and 173)
 
241
                        * GeForce 6200 (supported by driver 177 and 173)
 
242
                    
 
243
                        Driver 177 is selected.
 
244
                    '''
 
245
                    candidates.sort(reverse=True)
 
246
                    
 
247
                    
 
248
                    if 173 in candidates['nvidia'] and 177 in candidates['nvidia']:
 
249
                        '''
 
250
                        Prefer the stable driver
 
251
                        '''
 
252
                        choice = candidates['nvidia'][1]
 
253
                    else:
 
254
                        choice = candidates['nvidia'][0]
 
255
#                    if self.verbose and not self.printonly:
 
256
#                        print 'Recommended NVIDIA driver:', choice
 
257
                else:
 
258
                    '''
 
259
                    Otherwise, if there is no single driver version which works 
 
260
                    for all the available cards, the newest is selected.
 
261
                    
 
262
                    USE-CASE:
 
263
                        If a user has the following cards:
 
264
                        * GeForce 9300 (supported by driver 177 and 173)
 
265
                        * GeForce 1 (supported by driver 71)
 
266
                        * GeForce 2 (supported by driver 71)
 
267
                        
 
268
                        The most modern card has the highest priority since
 
269
                        no common driver can be found. The other 2 cards 
 
270
                        should use the open source driver
 
271
                    '''
 
272
                    
 
273
                    if 173 in occurrences and 177 in occurrences:
 
274
                        '''
 
275
                        Prefer the stable driver
 
276
                        '''
 
277
                        choice = occurrences[1]
 
278
                    else:
 
279
                        choice = occurrences[0]
 
280
#                    if self.verbose and not self.printonly:
 
281
#                        print 'Recommended NVIDIA driver:', choice
 
282
            else:#just one NVIDIA card
 
283
                '''
 
284
                The choice is easy if only one card is available and/or supported.
 
285
                
 
286
                The newest driver which supports the card is chosen.
 
287
                '''
 
288
                it = 0
 
289
                for card in self.driversForCards.keys():
 
290
                    if '1002' not in card:
 
291
                        position = it
 
292
                    it += 1
 
293
                
 
294
                drivers = self.driversForCards[self.driversForCards.keys()[position]]
 
295
                
 
296
                if 173 in drivers and 177 in drivers:
 
297
                    '''
 
298
                    Prefer the stable driver
 
299
                    '''
 
300
                    choice = drivers[1]
 
301
                else:
 
302
                    choice = drivers[0]
 
303
                
 
304
                #choice = self.driversForCards[self.driversForCards.keys()[position]][0]
 
305
            
 
306
            for card in self.driversForCards:
 
307
                if '1002' not in card:
 
308
                    for drv in self.driversForCards[card]:
 
309
                        compatibleDrivers.setdefault('nvidia', []).append('nvidia-glx-' + str(drv))
 
310
            
 
311
            choice = 'nvidia-glx-' + str(choice)
 
312
            
 
313
            recommendedDrivers['nvidia'] = choice
 
314
        
 
315
        
 
316
        
 
317
        if atiCardsNumber > 0:
 
318
            choice = self.driversForCards[self.driversForCards.keys()[0]][0]
 
319
            
 
320
            compatibleDrivers.setdefault('fglrx', []).append('xorg-driver-' + choice)
 
321
            recommendedDrivers['fglrx'] = 'xorg-driver-' + choice
 
322
            
 
323
        if atiCardsNumber == 0 and nvidiaCardsNumber == 0 :
 
324
            '''
 
325
            If no card is supported
 
326
            '''
 
327
            choice = None
 
328
            
 
329
            compatibleDrivers['nvidia'] = []
 
330
            recommendedDrivers['nvidia'] = choice
 
331
            compatibleDrivers['fglrx'] = []
 
332
            recommendedDrivers['fglrx'] = choice
 
333
        
 
334
        for driver in self.drivers:
 
335
            availableDrivers.setdefault(driver, [])
 
336
            temp = []
 
337
            for drv in self.drivers[driver]:
 
338
                temp.append(drv)
 
339
            
 
340
            if driver == 'nvidia':
 
341
                temp.sort(reverse=True)
 
342
                for drv in temp:
 
343
                    availableDrivers[driver].append('nvidia-glx-' + str(drv))
 
344
            elif driver == 'fglrx':
 
345
                for drv in temp:
 
346
                    availableDrivers[driver].append('xorg-driver-' + str(drv))
 
347
        
 
348
        for driver in availableDrivers:
 
349
            
 
350
            self.driverDetails.setdefault(driver, {})
 
351
            it = 0
 
352
            for drv in availableDrivers[driver]:
 
353
                self.driverDetails[driver].setdefault(it, {})
 
354
                self.driverDetails[driver][it]['name'] = drv
 
355
                try:
 
356
                    self.driverDetails[driver][it]['compatible'] = drv in compatibleDrivers[driver]
 
357
                    self.driverDetails[driver][it]['recommended'] = recommendedDrivers[driver] == drv
 
358
                except KeyError:
 
359
                    self.driverDetails[driver][it]['compatible'] = False
 
360
                    self.driverDetails[driver][it]['recommended'] = False
 
361
                
 
362
                
 
363
                it += 1
 
364
            
 
365
#        print 'DriverDetails =', self.driverDetails
 
366
##        print 'Compatible =', compatibleDrivers, '\nRecommended', recommendedDrivers
 
367
#        print 'All drivers =', availableDrivers#self.drivers['nvidia'].keys(), self.drivers['fglrx'].keys()
 
368
        return self.driverDetails
 
369
if __name__ == '__main__':
 
370
    a = HardwareDetection()
 
371
    print a.selectDriver()