Editing Programming N800 FM radio receiver

Warning: You are not logged in. Your IP address will be recorded in this page's edit history.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
-
The [[Nokia N800|N800]] internet tablet features a tea5761 FM tuner chip for listening
+
{{Midgard article}}
 +
 
 +
= How to control the FM tuner chip in the N800 =
 +
 
 +
== Introduction ==
 +
 
 +
The N800 internet tablet features a tea5761 FM tuner chip for listening
to FM radio. The kernel driver for this chip was written by Nokia and is GPL'd open source. You can look at it by downloading the Maemo kernel source code.
to FM radio. The kernel driver for this chip was written by Nokia and is GPL'd open source. You can look at it by downloading the Maemo kernel source code.
Line 6: Line 12:
== How to do it in Python ==
== How to do it in Python ==
-
By using Python's <code>ioctl</code> function, you can directly talk to the driver, so there is no need for wrapping some C library.
+
By using Pythons `ioctl` function, you can directly talk to the driver, so there's no need for wrapping some C library.
 +
 
=== Prerequisites ===
=== Prerequisites ===
Line 12: Line 19:
Let's first import the required modules:
Let's first import the required modules:
-
<source lang="python">
 
     import os                # for opening the device files
     import os                # for opening the device files
     import struct            # for packing and unpacking C structs
     import struct            # for packing and unpacking C structs
     from fcntl import ioctl  # for invoking ioctl calls on the device files
     from fcntl import ioctl  # for invoking ioctl calls on the device files
-
</source>
+
 
Since we are going to talk to kernel drivers, we have to use weird numbers. C users would just include the appropriate Linux kernel header files, but Python users have to do it by hand. So let's make some defines for kernel level ioctl stuff:
Since we are going to talk to kernel drivers, we have to use weird numbers. C users would just include the appropriate Linux kernel header files, but Python users have to do it by hand. So let's make some defines for kernel level ioctl stuff:
-
<source lang="python">
+
<pre>
     # kernel definitions for ioctl commands
     # kernel definitions for ioctl commands
     _IOC_NRBITS  = 8
     _IOC_NRBITS  = 8
Line 40: Line 46:
     _IOR  = lambda t,nr,size: _IOC(_IOC_READ, t, nr, size)
     _IOR  = lambda t,nr,size: _IOC(_IOC_READ, t, nr, size)
     _IOWR = lambda t,nr,size: _IOC(_IOC_READ | _IOC_WRITE, t, nr, size)
     _IOWR = lambda t,nr,size: _IOC(_IOC_READ | _IOC_WRITE, t, nr, size)
-
</source>
+
</pre>
-
The functions <code>_IOW</code>, <code>_IOR</code>, and <code>_IOWR</code> (macros in C) are used for write-access, read-access, and read-write-access, respectively. They basically just take some input values and compute a number which can be sent to the device driver. You can think of the number as the signature of the function which you want to call on the driver.
+
The functions ''_IOW'', ''_IOR'', and ''_IOWR'' (macros in C) are used for write-access, read-access, and read-write-access, respectively. They basically just take some input values and compute a number which can be sent to the device driver. You can think of the number as the signature of the function which you want to call on the driver.
Now we can generate those weird numbers for the V4L2 functions we are
Now we can generate those weird numbers for the V4L2 functions we are
going to invoke on the driver:
going to invoke on the driver:
-
<source lang="python">
+
<pre>
     # V4L2 stuff for accessing the tuner driver
     # V4L2 stuff for accessing the tuner driver
     _VIDIOC_G_TUNER    = _IOWR('V', 29, 84)
     _VIDIOC_G_TUNER    = _IOWR('V', 29, 84)
Line 61: Line 67:
     _V4L2_CID_BASE        = _V4L2_CTRL_CLASS_USER | 0x900
     _V4L2_CID_BASE        = _V4L2_CTRL_CLASS_USER | 0x900
     _V4L2_CID_AUDIO_MUTE  = _V4L2_CID_BASE + 9
     _V4L2_CID_AUDIO_MUTE  = _V4L2_CID_BASE + 9
-
</source>
+
</pre>
And last, let's define some numbers for working with the mixer device:
And last, let's define some numbers for working with the mixer device:
-
<source lang="python">
+
<pre>
     # mixer control constants
     # mixer control constants
     _SOUND_MIXER_FMRADIO        = 0x06
     _SOUND_MIXER_FMRADIO        = 0x06
     _SOUND_MIXER_READ            = 0x80044D00
     _SOUND_MIXER_READ            = 0x80044D00
     _SOUND_MIXER_WRITE          = 0xC0044D00
     _SOUND_MIXER_WRITE          = 0xC0044D00
-
</source>
+
 
 +
</pre>
 +
 
=== Opening the device ===
=== Opening the device ===
-
You gain access to the radio driver by opening the <code>/dev/radio</code> device. Since you need a system FD instead of a native Python FD, we use <code>os.open</code> instead of the built-in function <code>open</code>:
+
You gain access to the radio driver by opening the ''/dev/radio''
 +
device. Since you need a system FD instead of a native Python FD, we use
 +
''os.open'' instead of the built-in function ''open'':
-
<source lang="python">
 
     radio_fd = os.open("/dev/radio", os.O_RDONLY)
     radio_fd = os.open("/dev/radio", os.O_RDONLY)
-
</source>
 
-
If there's no tuner chip available (for example, if you run this code on the [[Nokia 770|770]] or the [[Nokia N810|N810]]), this line will throw an <code>OSError</code>. Don't forget to catch it.
+
If there's no tuner chip available (e.g. if you run this code on the 770 or the N810), this line will throw an `OSError`. Don't forget to catch it.
 +
 
=== Retrieving information about the FM tuner ===
=== Retrieving information about the FM tuner ===
-
Now it's time to talk to the FM tuner. We do this by placing an <code>ioctl</code>'' call on the device. <code>ioctl</code> calls consist of a number (those we have defined above), and some argument. The argument is a pointer for passing a C structure to the driver. The driver may read from this structure or write into it.
+
Now it's time to talk to the FM tuner. We do this by placing an ''ioctl'' call on the device. ''ioctl'' calls consist of a number (those we have defined above), and some argument. The argument is a pointer for passing a C structure to the driver. The driver may read from this structure or write into it.
-
A C structure can be built in Python with the <code>struct</code> module. Because the following <code>ioctl</code> call only retrieves information from the driver, we may provide an empty structure of the right size, like this:
+
A C structure can be built in Python with the ''struct'' module. Because the following ''ioctl'' call only retrieves information from the driver, we may provide an empty structure of the right size, like this:
-
<source lang="python">
 
     info = struct.pack("84x")
     info = struct.pack("84x")
-
</source>
 
-
Let's place the <code>ioctl</code>'' call, and retrieve the results. For unpacking the structure, we have to provide the correct format string (see <code>help(struct)</code> for details):
+
Let's place the ''ioctl'' call, and retrieve the results. For unpacking the
 +
structure, we have to provide the correct format string (see ''help(struct)'' for details):
-
<source lang="python">
+
<pre>
     data = ioctl(radio_fd, _VIDIOC_G_TUNER, info)
     data = ioctl(radio_fd, _VIDIOC_G_TUNER, info)
      
      
Line 109: Line 117:
     signal = fields[8]
     signal = fields[8]
     afc = fields[9]
     afc = fields[9]
-
</source>
+
</pre>
 +
 
 +
We should save the ''tuner_index'' and ''tuner_name'' for later, because this is how we address that particular tuner.
-
We should save the <code>tuner_index</code> and <code>tuner_name</code> for later, because this is how we address that particular tuner.
 
=== Setting the frequency ===
=== Setting the frequency ===
-
Next, we are going to tune into a radio station. For this, we have to set a frequency on the tuner. This is done by the V4L2 <code>_VIDIOC_G_FREQUENCY</code> <code>ioctl</code> invokation.
+
Next, we are going to tune into a radio station. For this, we have to set a frequency on the tuner. This is done by the V4L2 ''_VIDIOC_G_FREQUENCY'' ''ioctl'' invokation.
-
But a word of warning first: the driver does not take the frequency in kHz. You have to multiply your value with a constant factor first. The factor is either 0.016 or 16, depending on the device driver. On the N800, it is 16:
+
But a word of warning first: the driver doesn't take the frequency in KHz. You have to multiply your value with a constant factor first. The factor is either 0.016 or 16, depending on the device driver. On the N800, it is 16:
-
<source lang="python">
 
     FREQ_FACTOR = 16
     FREQ_FACTOR = 16
-
</source>
 
We are going to tune into 107.6 MHz now:
We are going to tune into 107.6 MHz now:
-
<source lang="python">
 
     freq = 107600 * FREQ_FACTOR
     freq = 107600 * FREQ_FACTOR
-
</source>
 
Set up the C structure to pass to the driver:
Set up the C structure to pass to the driver:
-
<source lang="python">
 
     data = struct.pack("LLL8L", tuner_index, tuner_type, freq, 0, 0, 0, 0, 0, 0, 0, 0)
     data = struct.pack("LLL8L", tuner_index, tuner_type, freq, 0, 0, 0, 0, 0, 0, 0, 0)
-
</source>
 
Pass it to the driver:
Pass it to the driver:
-
<source lang="python">
 
     ioctl(radio_fd, _VIDIOC_S_FREQUENCY, data)
     ioctl(radio_fd, _VIDIOC_S_FREQUENCY, data)
-
</source>
+
 
=== Unmuting the tuner and setting the volume ===
=== Unmuting the tuner and setting the volume ===
-
You still do not hear anything from the radio. This is because the tuner is muted and its volume is set to 0. Let's change this:
+
You still don't hear anything from the radio. This is because the tuner is muted and its volume is set to 0. Let's change this:
Unmuting is easy:
Unmuting is easy:
-
<source lang="python">
 
     data = struct.pack("Ll", _V4L2_CID_AUDIO_MUTE, 0)
     data = struct.pack("Ll", _V4L2_CID_AUDIO_MUTE, 0)
     ioctl(radio_fd, _VIDIOC_S_CTRL, data)
     ioctl(radio_fd, _VIDIOC_S_CTRL, data)
-
</source>
 
Changing the volume requires us to open the mixer device:
Changing the volume requires us to open the mixer device:
-
<source lang="python">
 
     mixer_fd = os.open("/dev/mixer", os.O_RDONLY)
     mixer_fd = os.open("/dev/mixer", os.O_RDONLY)
-
</source>
 
Now we can set the volume for the left and right speaker:
Now we can set the volume for the left and right speaker:
-
<source lang="python">
 
     left_volume = 50
     left_volume = 50
     right_volume = 50
     right_volume = 50
-
</source>
 
... and send it to the driver:
... and send it to the driver:
-
<source lang="python">
 
     data = struct.pack("bb", left_volume, right_volume)
     data = struct.pack("bb", left_volume, right_volume)
     ioctl(mixer, _SOUND_MIXER_WRITE | _SOUND_MIXER_FMRADIO, data)
     ioctl(mixer, _SOUND_MIXER_WRITE | _SOUND_MIXER_FMRADIO, data)
-
</source>
 
-
Do not forget to close the mixer device after this:
+
Don't forget to close the mixer device after this:
-
<source lang="python">
 
     os.close(mixer_fd)
     os.close(mixer_fd)
-
</source>
 
Now you can listen to the radio.
Now you can listen to the radio.
 +
=== Cleaning up ===
=== Cleaning up ===
Line 187: Line 180:
It's important to mute the radio, or you will hear it forever.
It's important to mute the radio, or you will hear it forever.
-
<source lang="python">
 
     data = struct.pack("Ll", _V4L2_CID_AUDIO_MUTE, 1)
     data = struct.pack("Ll", _V4L2_CID_AUDIO_MUTE, 1)
     ioctl(radio_fd, _VIDIOC_S_CTRL, data)
     ioctl(radio_fd, _VIDIOC_S_CTRL, data)
-
</source>
 
And don't forget to close the device:
And don't forget to close the device:
-
<source lang="python">
 
     os.close(radio_fd)
     os.close(radio_fd)
-
</source>
+
 
 +
 
== Code ready for use ==
== Code ready for use ==
-
There is a pure Python module available for controlling the FM tuner. It is based on the information here. You can get it from [http://pyfmradio.garage.maemo.org the PyFMRadio project page].
+
There is a pure Python module available for controlling the FM tuner. It's based on the information here.
 +
You can get it from [http://pyfmradio.garage.maemo.org the PyFMRadio project page].
[[Category:Development]]
[[Category:Development]]
-
[[Category:N8x0]]
+
[[Category:N800]]
-
[[Category:Python]]
+
[[Category:Midgard wiki]]

Learn more about Contributing to the wiki.


Please note that all contributions to maemo.org wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see maemo.org wiki:Copyrights for details). Do not submit copyrighted work without permission!


Cancel | Editing help (opens in new window)