Hi
On Fri, Apr 26, 2013 at 3:28 PM, David Henningsson david.henningsson@canonical.com wrote:
On 04/26/2013 03:00 PM, David Herrmann wrote:
Hi
On Thu, Apr 25, 2013 at 9:11 AM, David Henningsson david.henningsson@canonical.com wrote:
On 04/20/2013 08:14 PM, David Herrmann wrote:
Classic Wii Remotes provide a speaker on the device. We can stream PCM data, mute and change volume via special protocol requests and remote audio registers.
The device supports several different formats, but fully understood are only signed 8-bit PCM and something that looks like 4-bit Yamaha ADPCM. Theoretically, we can set data-rates from 183Hz up to 12MHz, but a realistic range for Bluetooth l2cap is 1500-4000 Hz.
Data is streamed as 20bytes blocks and must be sent in a constant rate. There is no way to read the current position. It would be way too slow, anyway.
Hi,
Fun project!
I'm not sure I'm qualified to answer, but I would have allocated a buffer of kernel memory, say 64K (or maybe less, since the sample rate is so low). That would be the buffer you expose to userspace, and userspace would set period and buffer sizes according to this buffer.
Your code doesn't really expose if/how you can tell if you're allowed to send 20 more bytes or not.
I cannot tell at all. That's the problem. I just send data at the expected rate and let the device deal with it. For instance with 8bit PCM at 2000 Hz that makes one 20byte packet every 10ms. That's 16kbit/s, which should work via Bluetooth l2cap.
Btw, since this is something bluetooth related, have you checked with the bluez/bluetoothd project, so it isn't easier to integrate that way instead of in a kernel driver?
That's how bluetooth audio is usually done.
Hehe, I'm involved in Bluetooth kernel development so I did check that first ;) The thing is, Nintendo did a pretty bad job with audio on the Wiimote. Bluetooth provides SCO channels specifically for audio but Nintendo tunnels audio through an HID connection, yay! On the other hand, the speaker is mostly used for small sound-effects so that explains why they didn't care for higher rates.
As l2cap+HID is handled in the kernel, I would have to provide a char-interface or sysfs PCM-stream or whatever for user-space so they can send HID reports with audio data while the kernel sends it's own HID reports for normal HID operation. That does work, but I thought if I already have all the device handling in the kernel, I should at least try to do it as sound-driver. If it doesn't work out I can always fall back to this option.
Even 10x the sample-rate (20kHz) with 160kbit/s should be possible, but the latency gets pretty high that I doubt I can do that with a 20byte buffer. I haven't figured out how big the real buffer of the device is so until then I expect it to be as low as 20 bytes (the proprietary driver does that, too).
If the transmission buffer overflows, I just drop packages so there is currently no easy way for me to see whether there's enough room for a next package or not. But the connection is reliable so once a packet is in the buffer, it's "guaranteed" to be transmitted in order to the device.
What's the expected thing to do if the connection is too slow? Should I pause and resume or drop packets?
Not sure what you mean here. Maybe bluetooth people know this problem better.
But assuming you have a timer running at a reasonable interval, that code would do something like:
if (ready_to_send_packet_to_wiimote()) { send_20_bytes_to_wiimote(kernel_buffer_ptr + offset); offset += 20; offset %= buffer_size; if (offset % period_size < 20) snd_pcm_period_elapsed(); }
Ah, ok, so I just send the data in the timer-interrupt and call snd_pcm_period_elapsed() if the fill-state gets smaller than 20 bytes? I guess snd_pcm_period_elapsed() is atomic so I can expect it to prepare the buffer so the next timer-interrupt is guaranteed to have a valid buffer or a cleared buffer?
snd_pcm_period_elapsed() just tells userspace that userspace can start filling up a new period in the ringbuffer. snd_pcm_period_elapsed() does not prepare anything.
E g, if userspace requests a buffer of 64K and period of 16K, you should call snd_pcm_period_elapsed() every time you cross a period boundary. Perhaps "offset % period_size < 20" can be more clearly written as "(offset / period_size) != (old_offset / period_size)", if that's more understandable?
Ah, now I understand. I thought the period-size is the maximum transmission size that I can use in the driver (20 in this case). Ok, your explanation helped. I think I understand the "period" concept now.
Apart from that, should I provide an mmap() interface for the buffer so I can map it into the clients address-space? Or should I prevent mmap() and let them use read/write? I guess I wouldn't need the .copy and .silence callbacks if I provide mmap(), right? And a vm_insert_pfn() would be quite simple to do..
What happens if user-space cannot keep up with the rates? Do I just continue sending garbage from the buffer or does the alsa core pause the transmission or silence it until user-space catches up?
Thanks David
I'm guessing that the hrtimer API would be appropriate, and some kind of RT priority. But I'm not really experienced in that area of kernel programming.
Yeah, the hrtimer should be perfect for this. I was just wondering whether the alsa-core already provides an optional timer for the drivers but it seems I need to do it myself.
Thanks a lot for the feedback! Lets see how it works out. David