PageRenderTime 60ms CodeModel.GetById 13ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 1ms

/bluetooth/widcomm.py

http://pybluez.googlecode.com/
Python | 790 lines | 750 code | 34 blank | 6 comment | 49 complexity | 7c7d4dc88c78f9fb8b53a2ad18a3f8fb MD5 | raw file
  1from .btcommon import *
  2import socket
  3import struct
  4import threading
  5import os
  6import _widcomm
  7
  8DEFAULT_MTU = 672
  9
 10def dbg (*args):
 11    return
 12    sys.stdout.write (*args)
 13    sys.stdout.write ("\n")
 14
 15def BD_ADDR_to_str (bda):
 16    return "%02X:%02X:%02X:%02X:%02X:%02X" % \
 17            (ord(bda[0]), ord(bda[1]), ord(bda[2]), 
 18             ord(bda[3]), ord(bda[4]), ord(bda[5])) 
 19
 20def str_to_BD_ADDR (s):
 21    digits = [ int (c, 16) for c in s.split(":") ]
 22    return struct.pack ("6B", *digits)
 23
 24class WCInquirer:
 25    DEVST_DOWN     = 0
 26    DEVST_UP       = 1
 27    DEVST_ERROR    = 2
 28    DEVST_UNLOADED = 3
 29    DEVST_RELOADED = 4
 30    
 31    def __init__ (self):
 32        self._wcinq = _widcomm._WCInquirer ()
 33
 34        port = self._wcinq.get_sockport ()
 35
 36        self.readsock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
 37        self.readsock.connect (("127.0.0.1", port))
 38        self._wcinq.accept_client ()
 39        
 40        self.recently_discovered = []
 41        self.inquiry_in_progress = False
 42        self.sdp_query_in_progress = False
 43
 44    def fileno ():
 45        return self.readsock.fileno ()
 46
 47    def start_inquiry (self):
 48        self.recently_discovered = []
 49        self.inquiry_in_progress = self._wcinq.start_inquiry ()
 50
 51    def read_msg (self):
 52        intsize = struct.calcsize ("=i")
 53        msg_type = struct.unpack ("=i", self.readsock.recv (intsize))[0]
 54
 55        if msg_type == _widcomm.INQ_DEVICE_RESPONDED:
 56            fmt = "=6s3s248si"
 57            data = self.readsock.recv (struct.calcsize (fmt))
 58            bda, devclass, bdname, connected = struct.unpack (fmt, data)
 59
 60            bdaddr = BD_ADDR_to_str (bda)
 61            bdname = bdname.strip ("\0")
 62            self.recently_discovered.append ((bdaddr, devclass, bdname, 
 63                    connected))
 64
 65        elif msg_type == _widcomm.INQ_INQUIRY_COMPLETE:
 66            fmt = "=ih"
 67            data = self.readsock.recv (struct.calcsize (fmt))
 68            success, num_responses = struct.unpack (fmt, data)
 69
 70            self.inquiry_in_progress = False
 71
 72        elif msg_type == _widcomm.INQ_DISCOVERY_COMPLETE:
 73            self.sdp_query_in_progress = False
 74
 75        elif msg_type == _widcomm.INQ_STACK_STATUS_CHANGE:
 76            fmt = "=i"
 77            data = self.readsock.recv (struct.calcsize (fmt))
 78            new_status = struct.unpack (fmt, data)[0]
 79
 80    def start_discovery (self, addr, uuid = None):
 81        bd_addr = str_to_BD_ADDR (addr)
 82        if uuid is not None:
 83            self.sdp_query_in_progress = \
 84                    self._wcinq.start_discovery (bd_addr, to_full_uuid (uuid))
 85        else:
 86            self.sdp_query_in_progress = \
 87                    self._wcinq.start_discovery (bd_addr)
 88        self.sdp_query_in_progress = True
 89
 90    def read_discovery_records (self, addr, uuid = None):
 91        if not is_valid_address (addr):
 92            raise ValueError ("invalid Bluetooth address")
 93        bd_addr = str_to_BD_ADDR (addr)
 94        if uuid is not None:
 95            dbg ("read_discovery_records (%s, %s)" % (addr, uuid))
 96            return self._wcinq.read_discovery_records (bd_addr,
 97                    to_full_uuid (uuid))
 98        else:
 99            return self._wcinq.read_discovery_records (bd_addr)
100
101    def is_device_ready (self):
102        return self._wcinq.is_device_ready ()
103
104    def get_local_device_address (self):
105        return self._wcinq.get_local_device_address ()
106
107inquirer = WCInquirer ()
108
109def discover_devices (duration=8, flush_cache=True, lookup_names=False, lookup_class=False):
110    inquirer.start_inquiry ()
111
112    while inquirer.inquiry_in_progress:
113        inquirer.read_msg ()
114
115    discovered = inquirer.recently_discovered[:]
116
117    if not lookup_names and not lookup_class:
118        return [ tup[0] for tup in discovered ]
119    if lookup_names and not lookup_class:
120        result = []
121        for bdaddr, devClass, bdName, bConnected in discovered:
122            if bdName:
123                result.append ((bdaddr, bdName))
124            else:
125                result.append ((bdAddr, None))
126        return result
127    if not lookup_names and lookup_class:
128        result = []
129        for bdaddr, devClass, bdName, bConnected in discovered:
130            hex = "%02X%02X%02X" % (ord(devClass[0]), ord(devClass[1]), ord(devClass[2]))
131            devClass = int(hex, 16)
132            result.append ((bdAddr, devClass))
133        return result
134    if lookup_names and lookup_class:
135        result = []
136        for bdaddr, devClass, bdName, bConnected in discovered:
137            hex = "%02X%02X%02X" % (ord(devClass[0]), ord(devClass[1]), ord(devClass[2]))
138            devClass = int(hex, 16)
139            if bdName:
140                result.append ((bdaddr, bdName, devClass))
141            else:
142                result.append ((bdAddr, None, devClass))
143        return result
144       
145def lookup_name (address, timeout=10):
146    discover_devices ()
147    for bdaddr, devClass, bdName, bConnected in inquirer.recently_discovered:
148        if bdaddr == address:
149            return bdName
150
151def advertise_service (sock, name, service_id = "", service_classes = [], \
152        profiles = [], provider = "", description = "", protocols = []):
153    sock._advertise_service (name, service_id, service_classes,
154            profiles, provider, description, protocols)
155
156def stop_advertising (sock):
157    sock._stop_advertising ()
158
159def find_service (name = None, uuid = None, address = None):
160    if address:
161        if address == "localhost": raise NotImplementedError
162        if not is_valid_address (address):
163            raise ValueError ("invalid Bluetooth address")
164        addresses = [ address ]
165    else:
166        addresses = discover_devices ()
167
168    if uuid and not is_valid_uuid (uuid):
169        raise ValueError ("invalid uuid ", uuid)
170
171    results = []
172    for addr in addresses:
173        inquirer.start_discovery (addr, uuid)
174        while inquirer.sdp_query_in_progress:
175            inquirer.read_msg ()
176        results.extend (inquirer.read_discovery_records (addr, uuid))
177
178    return results
179
180def _port_return_code_to_str (code):
181    k = { _widcomm.RFCOMM_SUCCESS : "Success",
182          _widcomm.RFCOMM_ALREADY_OPENED : "Port already opened",
183          _widcomm.RFCOMM_NOT_OPENED : "Connection not open",
184          _widcomm.RFCOMM_HANDLE_ERROR: "This error should never occur " \
185                                        "(HANDLE_ERROR) and is a stack bug",
186          _widcomm.RFCOMM_LINE_ERR: "Line error",
187          _widcomm.RFCOMM_START_FAILED: "Connection attempt failed",
188          _widcomm.RFCOMM_PAR_NEG_FAILED: "Parameter negotion (MTU) failed",
189          _widcomm.RFCOMM_PORT_NEG_FAILED: "Port negotiation failed",
190          _widcomm.RFCOMM_PEER_CONNECTION_FAILED: "Connection ended by remote "\
191                                                  "side",
192          _widcomm.RFCOMM_PEER_TIMEOUT: "Timeout by remote side",
193          _widcomm.RFCOMM_INVALID_PARAMETER: "Invalid parameter",
194          _widcomm.RFCOMM_UNKNOWN_ERROR: "Unknown error" }
195    if code in k:
196        return k[code]
197    else:
198        return "Invalid RFCOMM error code %s" % str (code)
199
200def _port_ev_code_to_str (code):
201    d = { _widcomm.PORT_EV_RXFLAG : "Received certain character",
202          _widcomm.PORT_EV_TXEMPTY : "Transmit Queue Empty",
203          _widcomm.PORT_EV_CTS : "CTS changed state",
204          _widcomm.PORT_EV_DSR : "DSR changed state",
205          _widcomm.PORT_EV_RLSD : "RLSD changed state",
206          _widcomm.PORT_EV_BREAK : "BREAK received",
207          _widcomm.PORT_EV_ERR : "Line status error occurred",
208          _widcomm.PORT_EV_RING : "Ring signal detected",
209          _widcomm.PORT_EV_CTSS : "CTS state",
210          _widcomm.PORT_EV_DSRS : "DSR state",
211          _widcomm.PORT_EV_RLSDS : "RLSD state",
212          _widcomm.PORT_EV_OVERRUN : "Receiver buffer overrun",
213          _widcomm.PORT_EV_TXCHAR : "Any character transmitted",
214          _widcomm.PORT_EV_CONNECTED : "RFCOMM connection established",
215          _widcomm.PORT_EV_CONNECT_ERR : "Was not able to establish " \
216                                         "connection or disconnected",
217          _widcomm.PORT_EV_FC : "Flow control enabled flag changed by remote",
218          _widcomm.PORT_EV_FCS : "Flow control status true = enabled" }
219    result = []
220    for k, v in list(d.items ()):
221        if code & k:
222            result.append (v)
223    if len (result) == 0:
224        return "Invalid event code %d" % code
225    else:
226        return "\n".join (result)
227
228def _sdp_checkraise (code):
229    if code == _widcomm.SDP_OK: return
230    elif code == _widcomm.SDP_COULD_NOT_ADD_RECORD:
231        raise BluetoothError ("Could not add SDP record")
232    elif code == _widcomm.SDP_INVALID_RECORD:
233        raise BluetoothError ("Invalid SDP record")
234    elif code == _widcomm.SDP_INVALID_PARAMETERS:
235        raise BluetoothError ("SDP: invalid parameters")
236    raise RuntimeError ("unknown SDP status code %s" % code)
237
238class BluetoothSocket:
239    def __init__ (self, proto = RFCOMM, _sockdata = None):
240        if not proto in [ RFCOMM, L2CAP ]:
241            raise ValueError ("invalid protocol")
242
243        self.proto = proto
244
245        if proto == RFCOMM:
246            self.bind            = self.rfcomm_bind
247            self.listen          = self.rfcomm_listen
248            self.accept          = self.rfcomm_accept
249            self.connect         = self.rfcomm_connect
250            self.send            = self.rfcomm_send
251            self.recv            = self.rfcomm_recv
252            self.close           = self.rfcomm_close
253            self.getsockname     = self.rfcomm_getsockname
254            self.setblocking     = self.rfcomm_setblocking
255            self.settimeout      = self.rfcomm_settimeout
256            self.gettimeout      = self.rfcomm_gettimeout
257            self.dup             = self.rfcomm_dup
258            self.makefile        = self.rfcomm_makefile
259            self.fileno          = self.rfcomm_fileno
260            self.__make_cobjects = self.__rfcomm_make_cobjects
261            self._advertise_service = self.__rfcomm_advertise_service
262
263            if _sockdata:
264                self._wc, self._if, self.readsock = _sockdata
265            else:
266                self.__make_cobjects ()
267
268            self.connected = self._wc.is_connected ()
269
270        elif proto == L2CAP:
271            dbg ("creating l2cap socket")
272            self.bind            = self.l2cap_bind
273            self.listen          = self.l2cap_listen
274            self.accept          = self.l2cap_accept
275            self.connect         = self.l2cap_connect
276            self.send            = self.l2cap_send
277            self.recv            = self.l2cap_recv
278            self.close           = self.l2cap_close
279            self.getsockname     = self.l2cap_getsockname
280            self.setblocking     = self.l2cap_setblocking
281            self.settimeout      = self.l2cap_settimeout
282            self.gettimeout      = self.l2cap_gettimeout
283            self.dup             = self.l2cap_dup
284            self.makefile        = self.l2cap_makefile
285            self.fileno          = self.l2cap_fileno
286            self.__make_cobjects = self.__l2cap_make_cobjects
287            self._advertise_service = self.__l2cap_advertise_service
288
289            if _sockdata:
290                self._wc, self._if, self.readsock = _sockdata
291                self.connected = True
292            else:
293                self.__make_cobjects ()
294                self.connected = False
295        else:
296            raise NotImplementedError ()
297
298        self.nonblocking = False
299        self.connecting = False
300        self.listening = False
301        self.bound = False
302        self.received_data = []
303        self.last_event_code = None
304        self.port = 0
305        self._sdpservice = None
306    
307    def _stop_advertising (self):
308        if not self._sdpservice:
309            raise BluetoothError ("not advertising any services")
310        self._sdpservice = None
311
312    def __rfcomm_make_cobjects (self):
313        self._wc = _widcomm._WCRfCommPort ()
314        self._if = _widcomm._WCRfCommIf ()
315        self.readsock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
316        self.readsock.connect (("127.0.0.1", self._wc.get_sockport ()))
317        self._wc.accept_client ()
318        
319    def rfcomm_read_msg (self):
320        intsize = struct.calcsize ("=i")
321        msg_type_data = self.readsock.recv (intsize)
322        msg_type = struct.unpack ("=i", msg_type_data)[0]
323
324        if msg_type == _widcomm.RFCOMM_DATA_RECEIVED:
325            datalen_fmt = "=i"
326            datalen_data = self.readsock.recv (struct.calcsize (datalen_fmt))
327            datalen = struct.unpack (datalen_fmt, datalen_data)[0]
328
329            self.received_data.append (self.readsock.recv (datalen))
330
331        elif msg_type == _widcomm.RFCOMM_EVENT_RECEIVED:
332            fmt = "=I"
333            data = self.readsock.recv (struct.calcsize (fmt))
334            code = struct.unpack (fmt, data)[0]
335            dbg ("event %X received" % code)
336            
337            if code & _widcomm.PORT_EV_CONNECTED:
338                self.connecting = False
339                self.listening = False
340                self.connected = True
341            if code & _widcomm.PORT_EV_CONNECT_ERR:
342                self.connecting = False
343                self.listening = False
344                self.connected = False
345                raise BluetoothError ("Connection failed")
346            if code & _widcomm.PORT_EV_RXFLAG:
347                dbg ("Rx flag")
348            if code & _widcomm.PORT_EV_TXEMPTY:
349                dbg ("Tx queue empty")
350            if code & _widcomm.PORT_EV_CTS:
351                dbg ("CTS changed state")
352            if code & _widcomm.PORT_EV_DSR:
353                dbg ("DSR changed state")
354            if code & _widcomm.PORT_EV_RLSD:
355                dbg ("RLSD changed state")
356            if code & _widcomm.PORT_EV_BREAK:
357                dbg ("BREAK received")
358            if code & _widcomm.PORT_EV_ERR:
359                dbg ("Line status error")
360            if code & _widcomm.PORT_EV_RING:
361                dbg ("Ring")
362            if code & _widcomm.PORT_EV_CTSS:
363                dbg ("CTS state")
364            if code & _widcomm.PORT_EV_DSRS:
365                dbg ("DSR state")
366            if code & _widcomm.PORT_EV_RLSDS:
367                dbg ("RLSD state")
368            if code & _widcomm.PORT_EV_OVERRUN:
369                dbg ("Receive buffer overrun")
370            if code & _widcomm.PORT_EV_TXCHAR:
371                dbg ("Data transmitted")
372            if code & _widcomm.PORT_EV_FC:
373                dbg ("Flow control changed by remote")
374            if code & _widcomm.PORT_EV_FCS:
375                dbg ("Flow control status true = enabled")
376
377            self.last_event_code = code
378
379    def rfcomm_bind (self, addrport):
380        addr, port = addrport
381
382        if len (addr): 
383            raise ValueError ("Widcomm stack can't bind to " \
384            "user-specified adapter")
385
386        result = self._if.assign_scn_value (RFCOMM_UUID, port)
387        if not result:
388            raise BluetoothError ("unable to bind to port")
389        self.bound = True
390        self.port = self._if.get_scn ()
391
392    def rfcomm_listen (self, backlog):
393        if self.connected: 
394            raise BluetoothError ("already connected")
395        if self.listening: 
396            raise BluetoothError ("already listening/connecting")
397        if backlog != 1:
398            raise ValueError ("Widcomm stack requires backlog == 1")
399
400        port = self._if.get_scn ()
401        self._if.set_security_level ("", _widcomm.BTM_SEC_NONE, True)
402        if not port:
403            raise BluetoothError ("not bound to a port")
404        result = self._wc.open_server (port, DEFAULT_MTU)
405        if result != _widcomm.RFCOMM_SUCCESS:
406            raise BluetoothError (_port_return_code_to_str (result))
407        self.listening = True
408
409    def rfcomm_accept (self):
410        if self.connected:
411            raise BluetoothError ("already connected")
412
413        while self.listening and not self.connected:
414            dbg ("waiting for connection")
415            self.rfcomm_read_msg ()
416
417        if self.connected:
418            port = self._if.get_scn ()
419
420            client_bdaddr = BD_ADDR_to_str (self._wc.is_connected ())
421            # XXX widcomm API doesn't provide a way to determine the RFCOMM
422            # channel number of the client
423            client_port = 0
424
425            # create a new socket object and give it ownership of the 
426            # wrapped C++ objects, since those are the ones actually connected
427            _sockdata = self._wc, self._if, self.readsock
428            clientsock = BluetoothSocket (RFCOMM, _sockdata)
429
430            # now create new C++ objects
431            self.__rfcomm_make_cobjects ()
432#            self.bind (("", port))
433#            self.listen (1)
434
435            return clientsock, (client_bdaddr, client_port)
436
437    def rfcomm_connect (self, addrport):
438        addr, port = addrport
439        dbg ("connecting to %s port %d" % (addr, port))
440        if not is_valid_address (addr):
441            raise ValueError ("invalid address %s" % addr)
442
443        self._if.assign_scn_value (RFCOMM_UUID, port)
444        self._if.set_security_level ("", _widcomm.BTM_SEC_NONE, False)
445
446        result = self._wc.open_client (port, str_to_BD_ADDR (addr), DEFAULT_MTU)
447        if result != _widcomm.RFCOMM_SUCCESS:
448            raise BluetoothError (_port_return_code_to_str (result))
449
450        self.connecting = True
451        while self.connecting:
452            self.rfcomm_read_msg ()
453
454        if not self._wc.is_connected ():
455            raise BluetoothError ("connection failed")
456
457
458    def rfcomm_send (self, data):
459        dbg ("sending: [%s]" % data)
460        status, written = self._wc.write (data)
461        if status == _widcomm.RFCOMM_SUCCESS:
462            dbg ("sent okay")
463            return written
464        else:
465            raise BluetoothError (_port_return_code_to_str (status))
466
467    def rfcomm_recv (self, numbytes):
468        if self.nonblocking and not self.received_data:
469            # XXX are we supposed to raise an exception, or just return None?
470            return None
471
472        while not self.received_data and self._wc.is_connected ():
473            self.rfcomm_read_msg ()
474
475        if self.received_data:
476            data = self.received_data.pop (0)
477            if len(data) > numbytes:
478                self.received_data.insert (0, data[numbytes:])
479                return data[:numbytes]
480            else:
481                return data
482
483    def rfcomm_close (self):
484        self._wc.close ()
485        self._wc = None
486        self.bound = False
487        self.connecting = False
488        self.listening = False
489        self.connected = False
490#        return bt.close (self._sockfd)
491
492    def rfcomm_getsockname (self):
493        if not self.bound:
494            raise BluetoothError ("Socket not bound")
495        addr = inquirer.get_local_device_address ()
496        port = self._if.get_scn ()
497        return addr, port
498
499    def rfcomm_setblocking (self, blocking):
500        self.nonblocking = not blocking
501        self.readsock.setblocking (blocking)
502
503    def rfcomm_settimeout (self, timeout):
504        raise NotImplementedError
505        pass
506#        if timeout < 0: raise ValueError ("invalid timeout")
507#
508#        if timeout == 0:
509#            self.setblocking (False)
510#        else:
511#            self.setblocking (True)
512#            # XXX this doesn't look correct
513#            timeout = 0 # winsock timeout still needs to be set 0
514#
515#        s = bt.settimeout (self._sockfd, timeout)
516#        self._timeout = timeout
517
518    def rfcomm_gettimeout (self):    
519        raise NotImplementedError
520#        if self._blocking and not self._timeout: return None
521#        return bt.gettimeout (self._sockfd)
522
523    def rfcomm_fileno (self):
524        return self.readsock.fileno ()
525
526    def rfcomm_dup (self):
527        raise NotImplementedError
528
529    def rfcomm_makefile (self):
530        raise NotImplementedError
531
532    def __rfcomm_advertise_service (self, name, service_id, 
533            service_classes, profiles, provider, description, 
534            protocols):
535        if self._sdpservice is not None:
536            raise BluetoothError ("Service already advertised")
537        if not self.listening:
538            raise BluetoothError ("Socket must be listening before advertised")
539        if protocols:
540            raise NotImplementedError ("extra protocols not yet supported in Widcomm stack")
541
542        self._sdpservice = _widcomm._WCSdpService ()
543        if service_classes:
544            service_classes = [ to_full_uuid (s) for s in service_classes ]
545            _sdp_checkraise (self._sdpservice.add_service_class_id_list ( \
546                    service_classes))
547        
548#        self._if.set_security_level (name, _widcomm.BTM_SEC_NONE, True)
549        _sdp_checkraise (self._sdpservice.add_rfcomm_protocol_descriptor ( \
550            self.port))
551        if profiles:
552            for uuid, version in profiles:
553                uuid = to_full_uuid (uuid)
554                _sdp_checkraise (self._sdpservice.add_profile_descriptor_list (\
555                        uuid, version))
556        _sdp_checkraise (self._sdpservice.add_service_name (name))
557        _sdp_checkraise (self._sdpservice.make_public_browseable ())
558
559    def __l2cap_make_cobjects (self):
560        dbg ("__l2cap_make_cobjects")
561        self._wc = _widcomm._WCL2CapConn ()
562        self._if = _widcomm._WCL2CapIf ()
563        self.readsock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
564        self.readsock.connect (("127.0.0.1", self._wc.get_sockport ()))
565        self._wc.accept_client ()
566        
567    def l2cap_read_msg (self):
568        intsize = struct.calcsize ("=i")
569        msg_type_data = self.readsock.recv (intsize)
570        msg_type = struct.unpack ("=i", msg_type_data)[0]
571
572        if msg_type == _widcomm.L2CAP_DATA_RECEIVED:
573            datalen_fmt = "=i"
574            datalen_data = self.readsock.recv (struct.calcsize (datalen_fmt))
575            datalen = struct.unpack (datalen_fmt, datalen_data)[0]
576            self.received_data.append (self.readsock.recv (datalen))
577
578        elif msg_type == _widcomm.L2CAP_INCOMING_CONNECTION:
579            result = self._wc.accept ()
580            if not result: raise BluetoothError ("accept() failed")
581
582        elif msg_type == _widcomm.L2CAP_REMOTE_DISCONNECTED:
583            dbg ("L2CAP_REMOTE_DISCONNECTED")
584            self.connecting = False
585            self.listening = False
586            self.connected = False
587
588        elif msg_type == _widcomm.L2CAP_CONNECTED:
589            self.connecting = False
590            self.listening = False
591            self.connected = True
592
593#        elif msg_type == _widcomm.PORT_EV_CONNECT_ERR:
594#            self.connecting = False
595#            self.listening = False
596#            raise BluetoothError ("Connection failed")
597
598    def l2cap_bind (self, addrport):
599        dbg ("l2cap_bind %s" % str(addrport))
600        addr, port = addrport
601
602        if len (addr): 
603            raise ValueError ("Widcomm stack can't bind to " \
604            "user-specified adapter")
605
606        result = self._if.assign_psm_value (L2CAP_UUID, port)
607        if not result:
608            raise BluetoothError ("unable to bind to port")
609        self.bound = True
610        self.port = self._if.get_psm ()
611        result = self._if.register ()
612        if not result:
613            raise BluetoothError ("register() failed")
614
615    def l2cap_listen (self, backlog):
616        dbg ("l2cap_listen %s" % backlog)
617        if self.connected:
618            raise BluetoothError ("already connected")
619        if self.listening:
620            raise BluetoothError ("already listening/connecting")
621        if backlog != 1:
622            raise ValueError ("Widcomm stack requires backlog == 1")
623
624        port = self._if.get_psm ()
625        self._if.set_security_level ("", _widcomm.BTM_SEC_NONE, True)
626        if not port:
627            raise BluetoothError ("not bound to a port")
628        result = self._wc.listen (self._if)
629        if not result:
630            raise BluetoothError ("listen() failed.  don't know why")
631        self.listening = True
632
633    def l2cap_accept (self):
634        dbg ("l2cap_accept")
635        if self.connected:
636            raise BluetoothError ("already connected")
637
638        while self.listening and not self.connected:
639            dbg ("waiting for connection")
640            self.l2cap_read_msg ()
641
642        if self.connected:
643            port = self._if.get_psm ()
644
645            client_bdaddr = BD_ADDR_to_str (self._wc.remote_bd_addr ())
646            # XXX widcomm API doesn't provide a way to determine the L2CAP
647            # PSM of the client
648            client_port = 0
649
650            # create a new socket object and give it ownership of the 
651            # wrapped C++ objects, since those are the ones actually connected
652            _sockdata = self._wc, self._if, self.readsock
653            clientsock = BluetoothSocket (L2CAP, _sockdata)
654
655            # now create new C++ objects
656            self.__l2cap_make_cobjects ()
657#            self.bind (("", port))
658#            self.listen (1)
659
660            return clientsock, (client_bdaddr, client_port)
661
662    def l2cap_connect (self, addrport):
663        addr, port = addrport
664        dbg ("connecting to %s port %d" % (addr, port))
665        if not is_valid_address (addr):
666            raise ValueError ("invalid address %s" % addr)
667
668        if not self._if.assign_psm_value (L2CAP_UUID, port):
669            raise BluetoothError ("Failed to assign PSM %d" % port)
670        if not self._if.set_security_level ("", _widcomm.BTM_SEC_NONE, False):
671            raise BluetoothError ("Failed to set security level")
672        if not self._if.register ():
673            raise BluetoothError ("Failed to register PSM")
674
675        self.connecting = True
676
677        if not self._wc.connect (self._if, str_to_BD_ADDR (addr)):
678            raise BluetoothError ("Connect failed")
679
680        while self.connecting:
681            self.l2cap_read_msg ()
682
683        if not self.connected:
684            raise BluetoothError ("connection failed")
685
686
687    def l2cap_send (self, data):
688        dbg ("sending: [%s]" % data)
689        status, written = self._wc.write (data)
690        if status:
691            dbg ("sent okay")
692            return written
693        else:
694            raise BluetoothError (_port_return_code_to_str (status))
695
696    def l2cap_recv (self, numbytes):
697        if self.nonblocking and not self.received_data:
698            # XXX are we supposed to raise an exception, or just return None?
699            return None
700
701        while not self.received_data and self.connected:
702            self.l2cap_read_msg ()
703
704        if self.received_data:
705            data = self.received_data.pop (0)
706            if len(data) > numbytes:
707                self.received_data.insert (0, data[numbytes:])
708                return data[:numbytes]
709            else:
710                return data
711
712    def l2cap_close (self):
713        self._wc.disconnect ()
714        self._if.deregister ()
715        self._wc = None
716        self.bound = False
717        self.connecting = False
718        self.listening = False
719        self.connected = False
720#        return bt.close (self._sockfd)
721
722    def l2cap_getsockname (self):
723        if not self.bound:
724            raise BluetoothError ("Socket not bound")
725        addr = inquirer.get_local_device_address ()
726        port = self._if.get_psm ()
727        return addr, port
728
729    def l2cap_setblocking (self, blocking):
730        self.nonblocking = not blocking
731        self.readsock.setblocking (blocking)
732
733    def l2cap_settimeout (self, timeout):
734        raise NotImplementedError
735#        if timeout < 0: raise ValueError ("invalid timeout")
736#
737#        if timeout == 0:
738#            self.setblocking (False)
739#        else:
740#            self.setblocking (True)
741#            # XXX this doesn't look correct
742#            timeout = 0 # winsock timeout still needs to be set 0
743#
744#        s = bt.settimeout (self._sockfd, timeout)
745#        self._timeout = timeout
746
747    def l2cap_gettimeout (self):    
748        raise NotImplementedError
749#        if self._blocking and not self._timeout: return None
750#        return bt.gettimeout (self._sockfd)
751
752    def l2cap_fileno (self):
753        return self.readsock.fileno ()
754
755    def l2cap_dup (self):
756        raise NotImplementedError
757#        return BluetoothSocket (self._proto, sockfd=bt.dup (self._sockfd))
758
759    def l2cap_makefile (self):
760        raise NotImplementedError
761
762    def __l2cap_advertise_service (self, name, service_id, 
763            service_classes, profiles, provider, description, 
764            protocols):
765        if self._sdpservice is not None:
766            raise BluetoothError ("Service already advertised")
767        if not self.listening:
768            raise BluetoothError ("Socket must be listening before advertised")
769        if protocols:
770            raise NotImplementedError ("extra protocols not yet supported in Widcomm stack")
771
772        self._sdpservice = _widcomm._WCSdpService ()
773        if service_classes:
774            service_classes = [ to_full_uuid (s) for s in service_classes ]
775            _sdp_checkraise (self._sdpservice.add_service_class_id_list ( \
776                service_classes))
777        _sdp_checkraise (self._sdpservice.add_l2cap_protocol_descriptor ( \
778                self.port))
779        if profiles:
780            for uuid, version in profiles:
781                uuid = to_full_uuid (uuid)
782                _sdp_checkraise (self._sdpservice.add_profile_descriptor_list (\
783                        uuid, version))
784        _sdp_checkraise (self._sdpservice.add_service_name (name))
785        _sdp_checkraise (self._sdpservice.make_public_browseable ())
786
787
788class DeviceDiscoverer:
789    def __init__ (self):
790        raise NotImplementedError