1 # -*- coding: ISO-8859-1 -*-
2 # python-wifi -- a wireless library to access wireless cards via python
3 # Copyright (C) 2004, 2005, 2006 RĂ³man Joost
6 # Mike Auty <m.auty@softhome.net> (Iwscanresult, Iwscan)
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public License
10 # as published by the Free Software Foundation; either version 2.1 of
11 # the License, or (at your option) any later version.
13 # This library is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 from struct import pack as struct_pack, \
24 unpack as struct_unpack, \
25 calcsize as struct_calcsize
27 from array import array
28 from math import ceil, log10
29 from fcntl import ioctl
30 from socket import AF_INET, SOCK_DGRAM, socket
31 from time import sleep
32 from re import compile
37 """ extract wireless device names of /proc/net/wireless
39 returns empty list if no devices are present
44 device = compile('[a-z]+[0-9]+')
47 f = open('/proc/net/wireless', 'r')
51 ifnames.append(device.search(line).group())
52 except AttributeError:
54 # if we couldn't lookup the devices, try to ask the kernel
56 ifnames = getConfiguredNICnames()
60 def getConfiguredNICnames():
61 """get the *configured* ifnames by a systemcall
63 >>> getConfiguredNICnames()
68 buff = array('c', '\0'*1024)
69 caddr_t, length = buff.buffer_info()
70 s = iwstruct.pack('iP', length, caddr_t)
72 result = iwstruct._fcntl(SIOCGIFCONF, s)
73 except IOError, (i, e):
76 # get the interface names out of the buffer
77 for i in range(0, 1024, 32):
78 ifname = buff.tostring()[i:i+32]
79 ifname = struct_unpack('32s', ifname)[0]
80 ifname = ifname.split('\0', 1)[0]
82 # verify if ifnames are really wifi devices
83 wifi = Wireless(ifname)
84 result = wifi.getAPaddr()
86 ifnames.append(ifname)
90 def makedict(**kwargs):
94 class Wireless(object):
95 """Access to wireless interfaces"""
97 def __init__(self, ifname):
98 self.sockfd = socket(AF_INET, SOCK_DGRAM)
100 self.iwstruct = Iwstruct()
103 """ returns accesspoint mac address
105 >>> from iwlibs import Wireless, getNICnames
106 >>> ifnames = getNICnames()
109 >>> wifi = Wireless(ifnames[0])
113 Test with non-wifi card:
114 >>> wifi = Wireless('eth0')
116 (95, 'Operation not supported')
118 Test with non-existant card:
119 >>> wifi = Wireless('eth2')
121 (19, 'No such device')
123 buff, s = self.iwstruct.pack_wrq(32)
124 i, result = self.iwstruct.iw_get_ext(self.ifname,
130 return self.iwstruct.getMAC(result)
132 def getBitrate(self):
133 """returns device currently set bit rate
135 >>> from iwlibs import Wireless
136 >>> wifi = Wireless('eth1')
137 >>> wifi.getBitrate()
140 i, result = self.iwstruct.iw_get_ext(self.ifname,
144 iwfreq = Iwfreq(result)
145 return iwfreq.getBitrate()
147 def getBitrates(self):
148 """returns the number of available bitrates for the device
150 >>> from iwlibs import Wireless
151 >>> wifi = Wireless('eth1')
152 >>> num, rates = wifi.getBitrates()
153 >>> num == len(rates)
156 range = Iwrange(self.ifname)
158 return (range.errorflag, range.error)
159 return (range.num_bitrates, range.bitrates)
161 def getChannelInfo(self):
162 """returns the number of channels and available frequency for
165 >>> from iwlibs import Wireless
166 >>> wifi = Wireless('eth1')
167 >>> num, rates = wifi.getChannelInfo()
168 >>> num == len(rates)
171 range = Iwrange(self.ifname)
173 return (range.errorflag, range.error)
174 return (range.num_channels, range.frequencies)
177 """get essid information
179 >>> from iwlibs import Wireless
180 >>> wifi = Wireless('eth1')
185 buff, s = self.iwstruct.pack_wrq(32)
186 i, result = self.iwstruct.iw_get_ext(self.ifname,
191 str = buff.tostring()
192 return str.strip('\x00')
194 def setEssid(self, essid):
196 raise NotImplementedError
197 if len(essid) > IW_ESSID_MAX_SIZE:
198 return "essid to big"
199 buff, s = self.iwstruct.pack_test(essid, 32)
200 i, result = self.iwstruct.iw_get_ext(self.ifname,
206 def getEncryption(self):
207 """get encryption information which is probably a string of '*',
210 as a normal user, you will get a 'Operation not permitted'
213 >>> from iwlibs import Wireless
214 >>> wifi = Wireless('eth1')
215 >>> wifi.getEncryption()
216 (1, 'Operation not permitted')
218 iwpoint = Iwpoint(self.ifname)
219 if iwpoint.errorflag:
220 return (iwpoint.errorflag, iwpoint.error)
221 return iwpoint.getEncryptionKey()
223 def getFragmentation(self):
224 """returns fragmentation threshold
226 It depends on what the driver says. If you have fragmentation
227 threshold turned on, you'll get an int. If it's turned of
228 you'll get a string: 'off'.
229 >>> from iwlibs import Wireless
230 >>> wifi = Wireless('eth1')
231 >>> wifi.getFragmentation()
234 iwparam = Iwparam(self.ifname, SIOCGIWFRAG)
235 if iwparam.errorflag:
236 return (iwparam.errorflag, iwparam.error)
237 return iwparam.getValue()
239 def getFrequency(self):
240 """returns currently set frequency of the card
242 >>> from iwlibs import Wireless
243 >>> wifi = Wireless('eth1')
244 >>> wifi.getFrequency()
247 i, r = self.iwstruct.iw_get_ext(self.ifname,
252 return iwfreq.getFrequency()
256 """returns currently set operation mode
258 >>> from iwlibs import Wireless
259 >>> wifi = Wireless('eth1')
263 i, result = self.iwstruct.iw_get_ext(self.ifname,
267 mode = self.iwstruct.unpack('i', result[:4])[0]
270 def setMode(self, mode):
271 """sets the operation mode """
273 this_modes = [x.lower() for x in modes]
275 wifimode = this_modes.index(mode)
277 return "Invalid operation mode!"
279 s = self.iwstruct.pack('I', wifimode)
280 i, result = self.iwstruct.iw_get_ext(self.ifname,
286 def getWirelessName(self):
287 """ returns wireless name
289 >>> from iwlibs import Wireless
290 >>> wifi = Wireless('eth1')
291 >>> wifi.getWirelessName()
294 i, result = self.iwstruct.iw_get_ext(self.ifname,
298 return result.split('\0')[0]
300 def getPowermanagement(self):
301 """returns power management settings
303 >>> from iwlibs import Wireless
304 >>> wifi = Wireless('eth1')
305 >>> wifi.getPowermanagement()
308 iwparam = Iwparam(self.ifname, SIOCGIWPOWER)
309 if iwparam.errorflag:
310 return (iwparam.errorflag, iwparam.error)
311 return iwparam.getValue()
314 def getRetrylimit(self):
315 """returns limit retry/lifetime
318 Most cards have MAC retransmissions, and some allow to set
319 the behaviour of the retry mechanism.
321 >>> from iwlibs import Wireless
322 >>> wifi = Wireless('eth1')
323 >>> wifi.getRetrylimit()
326 iwparam = Iwparam(self.ifname, SIOCGIWRETRY)
327 if iwparam.errorflag:
328 return (iwparam.errorflag, iwparam.error)
329 return iwparam.getValue()
332 """returns rts threshold
334 returns int, 'auto', 'fixed', 'off'
337 RTS/CTS adds a handshake before each packet transmission to
338 make sure that the channel is clear. This adds overhead, but
339 increases performance in case of hidden nodes or a large
340 number of active nodes. This parameter sets the size of the
341 smallest packet for which the node sends RTS; a value equal
342 to the maximum packet size disable the mechanism.
344 >>> from iwlibs import Wireless
345 >>> wifi = Wireless('eth1')
349 iwparam = Iwparam(self.ifname, SIOCGIWRTS)
350 if iwparam.errorflag:
351 return (iwparam.errorflag, iwparam.error)
352 return iwparam.getValue()
354 def getSensitivity(self):
355 """returns sensitivity information
358 This is the lowest signal level for which the hardware
359 attempt packet reception, signals weaker than this are
360 ignored. This is used to avoid receiving background noise,
361 so you should set it according to the average noise
362 level. Positive values are assumed to be the raw value used
363 by the hardware or a percentage, negative values are
366 >>> from iwlibs import Wireless
367 >>> wifi = Wireless('eth1')
368 >>> wifi.getSensitivity()
372 iwparam = Iwparam(self.ifname, SIOCGIWSENS)
373 if iwparam.errorflag:
374 return (iwparam.errorflag, iwparam.error)
375 return iwparam.getValue()
377 def getTXPower(self):
378 """returns transmit power in dBm
380 >>> from iwlibs import Wireless
381 >>> wifi = Wireless('eth1')
382 >>> wifi.getTXPower()
385 i, r = self.iwstruct.iw_get_ext(self.ifname,
390 return iwfreq.getTransmitPower()
392 def getStatistics(self):
393 """returns statistics information which can also be found in
396 iwstats = Iwstats(self.ifname)
397 if iwstats.errorflag > 0:
398 return (iwstats.errorflag, iwstats.error)
399 return [iwstats.status, iwstats.qual, iwstats.discard,
400 iwstats.missed_beacon]
403 """returns Iwscanresult objects, after a successful scan"""
404 iwscan = Iwscan(self.ifname)
408 class Iwstruct(object):
409 """basic class to handle iwstruct data """
413 self.sockfd = socket(AF_INET, SOCK_DGRAM)
415 def parse_data(self, fmt, data):
416 """ unpacks raw C data """
417 size = struct_calcsize(fmt)
420 str = data[idx:idx + size]
422 value = struct_unpack(fmt, str)
424 # take care of a tuple like (int, )
430 def pack(self, fmt, *args):
431 """ calls struct_pack and returns the result """
432 return struct_pack(fmt, *args)
434 def pack_wrq(self, buffsize):
435 """ packs wireless request data for sending it to the kernel """
437 # We need the address of our buffer and the size for it. The
438 # ioctl itself looks for the pointer to the address in our
439 # memory and the size of it.
440 # Dont change the order how the structure is packed!!!
441 buff = array('c', '\0'*buffsize)
442 caddr_t, length = buff.buffer_info()
443 s = struct_pack('Pi', caddr_t, length)
446 def pack_test(self, string, buffsize):
447 """ packs wireless request data for sending it to the kernel """
448 buffsize = buffsize - len(string)
449 buff = array('c', string+'\0'*buffsize)
450 caddr_t, length = buff.buffer_info()
451 s = struct_pack('Pii', caddr_t, length, 1)
454 def unpack(self, fmt, packed_data):
455 """ unpacks data with given format """
456 return struct_unpack(fmt, packed_data)
458 def _fcntl(self, request, args):
459 return ioctl(self.sockfd.fileno(), request, args)
461 def iw_get_ext(self, ifname, request, data=None):
462 """ read information from ifname """
463 # put some additional data behind the interface name
465 buff = IFNAMSIZE-len(ifname)
466 ifreq = ifname + '\0'*buff
469 ifreq = (ifname + '\0'*32)
472 result = self._fcntl(request, ifreq)
473 except IOError, (i, e):
476 return (0, result[16:])
478 def getMAC(self, packed_data):
479 """ extracts mac addr from packed data and returns it as str """
480 mac_addr = struct_unpack('xxBBBBBB', packed_data[:8])
481 return "%02X:%02X:%02X:%02X:%02X:%02X" % mac_addr
483 class Iwparam(object):
484 """class to hold iwparam data """
486 def __init__(self, ifname, ioctl):
487 # (i) value, (b) fixed, (b) disabled, (b) flags
500 """returns the value if not disabled """
504 if self.flags & IW_RETRY_TYPE == 0:
505 return self.getRLAttributes()
507 return self.getPMAttributes()
509 def getRLAttributes(self):
510 """returns a string with attributes determined by self.flags
514 def getPMAttributes(self):
515 """returns a string with attributes determined by self.flags
521 if self.flags & IW_POWER_MIN == 0:
523 if self.flags & IW_POWER_MAX == 0:
527 if self.flags & IW_POWER_TIMEOUT == 0:
531 # Value with or without units
532 # IW_POWER_RELATIVE - value is *not* in s/ms/us
533 if self.flags & IW_POWER_RELATIVE:
534 result += "%f" %(float(self.value)/MEGA)
536 if self.value >= MEGA:
537 result += "%fs" %(float(self.value)/MEGA)
538 elif self.value >= KILO:
539 result += "%fms" %(float(self.value)/KILO)
541 result += "%dus" % self.value
546 iwstruct = Iwstruct()
547 i, r = iwstruct.iw_get_ext(self.ifname,
554 def _parse(self, data):
555 """ unpacks iwparam data """
556 iwstruct = Iwstruct()
557 self.value, self.fixed, self.disabled, self.flags =\
558 iwstruct.parse_data(self.fmt, data)
560 class Iwfreq(object):
561 """ class to hold iwfreq data
562 delegates to Iwstruct class
565 def __init__(self, data=None):
568 self.frequency = self.parse(data)
571 self.iwstruct = Iwstruct()
573 def __getattr__(self, attr):
574 return getattr(self.iwstruct, attr)
576 def parse(self, data):
577 """ unpacks iwparam"""
579 size = struct_calcsize(self.fmt)
580 m, e, i, pad = struct_unpack(self.fmt, data[:size])
581 # XXX well, its not *the* frequency - we need a better name
585 return float(m)*10**e
587 def getFrequency(self):
588 """returns Frequency (str)
590 data - binary data returned by systemcall (iw_get_ext())
592 freq = self.frequency
595 return "%0.3fGHz" %(freq/GIGA)
598 return "%0.3fMHZ" %(freq/MEGA)
601 return "%0.3fKHz" %(freq/KILO)
603 def getBitrate(self):
604 """ returns Bitrate in Mbit
606 data - binary data returned by systemcall (iw_get_ext())
608 bitrate = self.frequency
611 return "%i Gb/s" %(bitrate/GIGA)
614 return "%i Mb/s" %(bitrate/MEGA)
617 return "%i Kb/s" %(bitrate/KILO)
619 def getTransmitPower(self):
620 """ returns transmit power in dbm """
621 # XXX something flaky is going on with m and e
622 # eg. m = 50 and e should than be 0, because the number is stored in
623 # m and don't needs to be recalculated
624 return "%i dBm" %self.mw2dbm(self.frequency/10)
626 def getChannel(self, freq):
627 """returns channel information given by frequency
629 returns None if frequency can't be converted
630 freq = frequency to convert (int)
631 iwrange = Iwrange object
640 #13 Channels beginning at 2.412GHz and inreasing by 0,005 GHz steps
641 for i in range(0,12):
642 cur = float( 2.412 + ( i * 0.005 ) )
644 # Channel 14 need special actions ;)
648 if str(freq) in lut.keys():
649 return lut[str(freq)]
654 def mw2dbm(self, mwatt):
655 """ converts mw to dbm(float) """
656 return ceil(10.0 * log10(mwatt))
658 def _setFrequency(self, list):
659 """sets self.frequency by given list
661 currently only used by Iwrange
663 assert len(list) == 4
668 self.frequency = m #float(m)*10**e
670 class Iwstats(object):
671 """ class to hold iwstat data """
673 def __init__(self, ifname):
674 # (2B) status, 4B iw_quality, 6i iw_discarded
677 self.qual = Iwquality()
679 self.missed_beacon = 0
686 iwstruct = Iwstruct()
687 buff, s = iwstruct.pack_wrq(32)
688 i, result = iwstruct.iw_get_ext(self.ifname,
694 self._parse(buff.tostring())
696 def _parse(self, data):
697 """ unpacks iwstruct data """
700 iwstats_data = struct.parse_data(self.fmt, data)
702 self.status = iwstats_data[0:2]
703 self.qual.quality, self.qual.sl, self.qual.nl,\
704 self.qual.flags = iwstats_data[2:6]
705 nwid, code, frag, retries, flags = iwstats_data[6:11]
706 self.missed_beacon = iwstats_data[11:12][0]
707 self.discard = makedict(nwid=nwid, code=code,
708 fragment=frag, retries=retries, misc=flags)
710 class Iwquality(object):
711 """ class to hold iwquality data """
720 def parse(self, data):
721 """ unpacks iwquality data """
723 qual, sl, nl, flags = struct.parse_data(self.fmt, data)
725 # compute signal and noise level
726 self.signal_level = sl
727 self.noise_level = nl
729 # asign the other values
733 def setValues(self, list):
734 """ assigns values given by a list to our attributes """
735 attributes = ["quality", "signallevel", "noise_level",
737 assert len(list) == 4
739 for i in range(len(list)):
740 setattr(self, attributes[i], list[i])
742 def getSignallevel(self):
743 """ returns signal level """
746 def setSignallevel(self, sl):
747 """ sets signal level """
749 signallevel = property(getSignallevel, setSignallevel)
751 def getNoiselevel(self):
752 """ returns noise level """
753 return self.nl - 0x100
755 def setNoiselevel(self):
756 raise NotImplementedError
758 noiselevel = property(getNoiselevel, setNoiselevel)
760 class Iwpoint(object):
761 """ class to hold iwpoint data """
763 def __init__(self, ifname):
767 # (4B) pointer to data, H length, H flags
774 def __getattr__(self, attr):
775 return getattr(self.iwstruct, attr)
778 iwstruct = Iwstruct()
779 buff, s = iwstruct.pack_wrq(32)
780 i, result = iwstruct.iw_get_ext(self.ifname,
788 def getEncryptionKey(self):
789 """ returns encryption key as '**' or 'off' as str """
790 if self.flags & IW_ENCODE_DISABLED != 0:
792 elif self.flags & IW_ENCODE_NOKEY != 0:
793 # a key is set, so print it
794 return '**' * self.fields
796 def _parse(self, data):
797 """ unpacks iwpoint data
799 iwstruct = Iwstruct()
800 ptr, ptr, ptr, ptr, self.fields, self.flags =\
801 iwstruct.parse_data(self.fmt, data)
802 self.key = [ptr, ptr, ptr, ptr]
804 class Iwrange(object):
805 """holds iwrange struct """
806 IW_MAX_FREQUENCIES = 32
808 def __init__(self, ifname):
809 self.fmt = "iiihb6ii4B4Bi32i2i2i2i2i3h8h2b2bhi8i2b3h2i2ihB17x"\
810 + self.IW_MAX_FREQUENCIES*"ihbb"
819 # nwid (or domain id)
820 self.min_nwid = self.max_nwid = 0
822 # frequency for backward compatibility
823 self.old_num_channels = self.old_num_frequency = self.old_freq = 0
825 # signal level threshold
829 self.max_qual = Iwquality()
830 self.avg_qual = Iwquality()
833 self.num_bitrates = 0
837 self.min_rts = self.max_rts = 0
839 # fragmention threshold
840 self.min_frag = self.max_frag = 0
843 self.min_pmp = self.max_pmp = 0
844 self.min_pmt = self.max_pmt = 0
845 self.pmp_flags = self.pmt_flags = self.pm_capa = 0
848 self.encoding_size = 0
849 self.num_encoding_sizes = self.max_encoding_tokens = 0
850 self.encoding_login_index = 0
853 self.txpower_capa = self.num_txpower = self.txpower = 0
855 # wireless extension version info
856 self.we_vers_compiled = self.we_vers_src = 0
858 # retry limits and lifetime
859 self.retry_capa = self.retry_flags = self.r_time_flags = 0
860 self.min_retry = self.max_retry = 0
861 self.min_r_time = self.max_r_time = 0
864 self.num_channels = self.num_frequency = 0
865 self.frequencies = []
869 """updates Iwrange object by a system call to the kernel
870 and updates internal attributes
872 iwstruct = Iwstruct()
873 buff, s = iwstruct.pack_wrq(640)
874 i, result = iwstruct.iw_get_ext(self.ifname,
880 data = buff.tostring()
883 def _parse(self, data):
885 result = struct.parse_data(self.fmt, data)
887 # XXX there is maybe a much more elegant way to do this
888 self.throughput, self.min_nwid, self.max_nwid = result[0:3]
889 self.old_num_channels, self.old_num_frequency = result[3:5]
890 self.old_freq = result[5:11]
891 self.sensitivity = result[11]
892 self.max_qual.setValues(result[12:16])
893 self.avg_qual.setValues(result[16:20])
894 self.num_bitrates = result[20] # <- XXX
895 raw_bitrates = result[21:53]
896 for rate in raw_bitrates:
898 iwfreq.frequency = rate
899 br = iwfreq.getBitrate()
901 self.bitrates.append(br)
903 self.min_rts, self.max_rts = result[53:55]
904 self.min_frag, self.max_frag = result[55:57]
905 self.min_pmp, self.max_pmp = result[57:59]
906 self.min_pmt, self.max_pmt = result[59:61]
907 self.pmp_flags, self.pmt_flags, self.pm_capa = result[61:64]
908 self.encoding_size = result[64:72]
909 self.num_encoding_sizes, self.max_encoding_tokens = result[72:74]
910 self.encoding_login_index = result[74:76]
911 self.txpower_capa, self.num_txpower = result[76:78]
912 self.txpower = result[78:86]
913 self.we_vers_compiled, self.we_vers_src = result[86:88]
914 self.retry_capa, self.retry_flags, self.r_time_flags = result[88:91]
915 self.min_retry, self.max_retry = result[91:93]
916 self.min_r_time, self.max_r_time = result[93:95]
917 self.num_channels = result[95]
918 self.num_frequency = result[96]
921 i = self.num_frequency
922 for x in range(0, len(freq), 4):
924 iwfreq._setFrequency(freq[x:x+4])
925 fq = iwfreq.getFrequency()
927 self.frequencies.append(fq)
932 class Iwscan(object):
933 """class to handle AP scanning"""
935 def __init__(self, ifname):
937 self.range = Iwrange(ifname)
943 def scan(self, fullscan=True):
944 """Completes a scan for available access points,
945 and returns them in Iwscanresult format
947 fullscan: If False, data is read from a cache of the last scan
948 If True, a scan is conducted, and then the data is read
950 # By default everything is fine, do not wait
954 if self.errorflag > EPERM:
955 raise RuntimeError, 'setScan failure ' + str(self.errorflag) + " " + str(self.error)
957 elif self.errorflag < EPERM:
958 # Permission was NOT denied, therefore we must WAIT to get results
963 result = self.getScan()
965 if result < 0 or self.errorflag != 0:
966 raise RuntimeError, 'getScan failure ' + str(self.errorflag) + " " + str(self.error)
972 """Triggers the scan, if we have permission
974 iwstruct = Iwstruct()
975 s = iwstruct.pack('Pii', 0, 0, 0)
976 i, result = iwstruct.iw_get_ext(self.ifname,
984 """Retreives results, stored from the most recent scan
985 Returns 0 if successful, a delay if the data isn't ready yet
986 or -1 if something really nasty happened
988 iwstruct = Iwstruct()
990 bufflen = IW_SCAN_MAX_DATA
992 # Keep resizing the buffer until it's large enough to hold the scan
994 buff, s = iwstruct.pack_wrq(bufflen)
995 i, result = iwstruct.iw_get_ext(self.ifname,
999 pbuff, newlen = iwstruct.unpack('Pi', s)
1000 if bufflen < newlen:
1003 bufflen = bufflen * 2
1012 pbuff, reslen = iwstruct.unpack('Pi', s)
1014 # Initialize the stream, and turn it into an enumerator
1015 self.aplist = self._parse(buff.tostring())
1018 def _parse(self, data):
1019 """Parse the event stream, and return a list of Iwscanresult objects
1021 iwstruct = Iwstruct()
1025 # Run through the stream, until broken
1027 # If we're the stream doesn't have enough space left for a header, break
1028 if len(data) < IW_EV_LCP_LEN:
1032 length, cmd = iwstruct.unpack('HH', data[:4])
1033 # If the header says the following data is shorter than the header, then break
1034 if length < IW_EV_LCP_LEN:
1037 # Put the events into their respective result data
1038 if cmd == SIOCGIWAP:
1039 if scanresult is not None:
1040 aplist.append(scanresult)
1041 scanresult = Iwscanresult(data[IW_EV_LCP_LEN:length], self.range)
1042 elif scanresult is None:
1043 raise RuntimeError, 'Attempting to add an event without AP data'
1045 scanresult.addEvent(cmd, data[IW_EV_LCP_LEN:length])
1047 # We're finished with the preveious event
1048 data = data[length:]
1050 # Don't forgset the final result
1051 if scanresult.bssid != "00:00:00:00:00:00":
1052 aplist.append(scanresult)
1054 raise RuntimeError, 'Attempting to add an AP without a bssid'
1057 class Iwscanresult(object):
1058 """An object to contain all the events associated with a single scanned AP
1061 def __init__(self, data, range):
1062 """Initialize the scan result with the access point data"""
1063 self.iwstruct = Iwstruct()
1065 self.bssid = "%02X:%02X:%02X:%02X:%02X:%02X" % struct_unpack('BBBBBB', data[2:8])
1069 self.quality = Iwquality()
1070 self.frequency = None
1073 self.protocol = None
1075 def addEvent(self, cmd, data):
1076 """Attempts to add the data from an event to a scanresult
1077 Only certain data is accept, in which case the result is True
1078 If the event data is invalid, None is returned
1079 If the data is valid but unused, False is returned
1081 if cmd <= SIOCIWLAST:
1082 if cmd < SIOCIWFIRST:
1084 elif cmd >= IWEVFIRST:
1090 if cmd == SIOCGIWESSID:
1091 self.essid = data[4:]
1092 elif cmd == SIOCGIWMODE:
1093 self.mode = modes[self.iwstruct.unpack('i', data[:4])[0]]
1094 elif cmd == SIOCGIWRATE:
1095 # TODO, deal with multiple rates, or at least the highest rate
1096 freqsize = struct_calcsize("ihbb")
1097 while len(data) >= freqsize:
1098 iwfreq = Iwfreq(data)
1099 self.rate.append(iwfreq.getBitrate())
1100 data = data[freqsize:]
1101 elif cmd == IWEVQUAL:
1102 self.quality.parse(data)
1103 elif cmd == SIOCGIWFREQ:
1104 self.frequency = Iwfreq(data)
1105 elif cmd == SIOCGIWENCODE:
1107 elif cmd == IWEVCUSTOM:
1108 self.custom.append(data[1:])
1109 elif cmd == SIOCGIWNAME:
1110 self.protocol = data[:len(data)-2]