linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Henryk Plötz" <henryk@ploetzli.ch>
To: bluez-devel@lists.sourceforge.net
Subject: Re: [Bluez-devel] Kernel panic with SCO socket - Debian kernel 2.6.15-8
Date: Sat, 18 Mar 2006 20:12:20 +0100	[thread overview]
Message-ID: <20060318201220.46312b44.henryk@ploetzli.ch> (raw)
In-Reply-To: <441C3CAB.5030806@silicom.fr>


[-- Attachment #1.1: Type: text/plain, Size: 1181 bytes --]

Moin,

Am Sat, 18 Mar 2006 18:00:27 +0100 schrieb Fabien Chevalier -:

> I wrote a sample program that mimics the establishment of a Headset 
> Profile like connection.

Hehe, I recently did something similar (in Python, though) ...

> The thing basically works... exect after 1or 2 minutes playback, the 
> program suddenly exits.
> Then i just have a few seconds left and *bang* . Kernel Panic :-(

... and had similar results. Just look at the memory consumption when
this happens. Apparently all incoming SCO data is buffered in kernel
memory for you to read, so you *must* read it, even if you only ever
intend to send data.

I'd also suggest you simply use select() to wait for incoming data and
then only send something when you received (and read!) something. This
nicely gives the right timing without guessing the delay parameter.


For reference I've attached my headset emulator (to be used with
pybluez): It discards incoming audio and sends a pure sine wave.
-- 
Henryk Plötz
Grüße aus Berlin
~~~~~~~ Un-CDs, nein danke! http://www.heise.de/ct/cd-register/ ~~~~~~~
~ Help Microsoft fight software piracy: Give Linux to a friend today! ~

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: headset.py --]
[-- Type: text/x-python; name="headset.py", Size: 6900 bytes --]

#!/bin/env python

import bluetooth, select, math, struct

class Headset:
    MAX_TRIES = 3
    MAX_LINE_LENGTH = 100
    FREQUENCY = 19.8
    
    def __init__(self):
        self.local_address = "00:80:98:34:38:9B"
        self.connection = None
        self.SCOserver = None
        self.SCOconnection = None
        self.unhandled = ""
        self.peer = None
        self.waiting_for_OK = None
        self._audio_init = False
        
        self.bind_RFCOMM()
    
    def bind_RFCOMM(self):
        tries = 0
        self.serversock = bluetooth.BluetoothSocket(proto=bluetooth.RFCOMM)
        
        while tries < Headset.MAX_TRIES:
            tries = tries + 1
            try:
                port = bluetooth.get_available_port(bluetooth.RFCOMM)
                self.serversock.bind( (self.local_address, port) )
                self.serversock.listen(1)
                self.local_port = port
                break
            except bluetooth.BluetoothError, e:
                if tries >= Headset.MAX_TRIES:
                    raise
            
        bluetooth.advertise_service(self.serversock, "Python-Headset", 
            profiles = [bluetooth.HEADSET_PROFILE],
            service_classes = [bluetooth.HEADSET_CLASS])
        
        print "Bound to '%s' on %s" % (self.local_address, self.local_port)
    
    def bind_SCO(self):
        self.SCOserver = bluetooth.BluetoothSocket(proto=bluetooth.SCO)
        self.SCOserver.bind( (self.local_address,) )
        self.SCOserver.listen(1)
    
    def close(self):
        print "Closing"
        
        try: self.SCOconnection.close()
        except: pass
        
        try: self.SCOserver.close()
        except: pass
        
        try: bluetooth.stop_advertising(self.serversock)
        except: pass
        
        try: self.serversock.close()
        except: pass
        
        try: self.connection.close()
        except: pass
    
    def run(self):
        while True:
            self.process_event()
    
    def process_event(self, timeout = None):
        rlist = [self.serversock, ]; wlist = []; xlist = []
        if self.connection:
            rlist.append(self.connection)
            xlist.append(self.connection)
        
        if self.SCOserver:
            rlist.append(self.SCOserver)
        
        if self.SCOconnection:
            rlist.append(self.SCOconnection)
        
        rlist, wlist, xlist = select.select(rlist, wlist, xlist, timeout)
        
        if self.serversock in rlist:
            self.accept_connection()
        
        if self.SCOserver in rlist:
            self.accept_SCO()
        
        if self.SCOconnection in rlist:
            self.do_SCO_cycle()
        
        if self.connection:
            if self.connection in xlist:
                print "X on connection"
            if self.connection in rlist:
                self.read_control()
    
    def accept_connection(self):
        conn, address = self.serversock.accept()
        print "Have connection by", address
        self.peer = address[0]
        self.connection = conn
        
        self.waiting_for_OK = True
        self.write_control("AT+CKPD=200")
    
    def accept_SCO(self):
        conn, address = self.SCOserver.accept()
        if address != self.peer:
            print "Warning: unauthorized SCO connection by %s (!=%s). Closing." % (address, self.peer)
            conn.close()
        else:
            self.SCOconnection = conn
            print "Have SCO connection"
    
    def read_control(self):
        try:
            data = self.unhandled + self.connection.recv(65535)
        except bluetooth.BluetoothError, e:
            if str(e)[:5] == "(104,":
                self.close_control()
                return
            else:
                raise
        
        line = None
        
        if data[0] == "\r" and data[1] == "\n":
            data = data[2:]
            pos = None
            for i in range(len(data)):
                if i > 0 and data[i-1] == "\r" and data[i] == "\n":
                    pos = i+1
                    break
            
            if pos is not None:
                line = data[:pos-2]
                data = data[pos:]
        elif data[:3] == "AT+":
            pos = None
            for i in range(len(data)):
                if data[i] == "\r":
                    pos = i
                    break
            
            if pos is not None:
                line = data[:pos]
                data = data[pos+1:]
        
        self.unhandled = data
        if line is not None:
            self.handle_command(line)
        
        if len(self.unhandled) > Headset.MAX_LINE_LENGTH:
            raise IOError("Maximum line length exceeded. Shouldn't happen.")
    
    def write_control(self, data):
        self.connection.send(data+"\r")
    
    def handle_command(self, command):
        if self.waiting_for_OK and command == "OK":
            self.waiting_for_OK = False
            self.bind_SCO()
            print "Established"
        else:
            print "Ignoring '%s'" % command
    
    def do_SCO_cycle(self):
        try:
            data = self.SCOconnection.recv(1024)
        except bluetooth.BluetoothError, e:
            if str(e)[:5] == "(104,":
                self.close_SCO()
                return
            else:
                raise
        
        if not hasattr(self, "_i"): self._i = 0
        print "Have %s bytes SCO data (%i)" % (len(data), self._i)
        self._i = self._i + 1
        
        response = []
        while len(response) < len(data)/2:
            response.append(self.nextaudio())
        
        self.SCOconnection.send("".join(response))
    
    def nextaudio(self):
        if not self._audio_init:
            self._x = 0.0
            self._audio_init = True
        sample = math.sin( math.radians( self._x ) ) * (1<<14)
        self._x = (self._x + Headset.FREQUENCY) % 360.0
        return struct.pack("<h", sample)
    
    def close_SCO(self):
        self.SCOconnection.close()
        self.SCOconnection = None
        
        print "SCO channel closed"
    
    def close_control(self):
        if self.SCOconnection:
            self.close_SCO()
        
        self.SCOserver.close()
        self.SCOserver = None
        self.waiting_for_OK = None
        self.connection.close()
        self.connection = None
        self.peer = None
        
        print "Control channel closed"

if __name__ == "__main__":
    headset = Headset()
    
    try:
        try:
            headset.run()
        except KeyboardInterrupt:
            pass
    finally:
        headset.close()
    

[-- Attachment #2: Type: application/pgp-signature, Size: 191 bytes --]

  reply	other threads:[~2006-03-18 19:12 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-03-18 17:00 [Bluez-devel] Kernel panic with SCO socket - Debian kernel 2.6.15-8 Fabien Chevalier -
2006-03-18 19:12 ` Henryk Plötz [this message]
2006-03-19 11:16   ` Fabien Chevalier -

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20060318201220.46312b44.henryk@ploetzli.ch \
    --to=henryk@ploetzli.ch \
    --cc=bluez-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).