The Devil's in the Details
The last important I/O function to be shown is select(), one of the most interesting parts of Unix, in our opinion.
The select() call is used to wait for a device to become ready, and is one of the most scary functions for the novice C programmer. While its use from within an application is not shown here, the driver-specific part of the system call is shown, and its most impressive feature is its compactness.
Here's the full code:
Static int skel_select(struct inode *inode,
struct file *file,
int sel_type,
select_table *wait) {
Skel_Clientdata *data=filp->private_data;
Skel_Board *board=data->board;
if (sel_type==SEL_IN) {
if (! SKEL_IBUF_EMPTY (board))
/* readable */
return 1;
skel_tell_hw_we_ask_for_data;
select_wait(&(hwp->skel_wait_iq), wait);
/* not readable */
return 0;
}
if (sel_type==SEL_OUT) {
if (! SKEL_OBUF_FULL (board))
return 1; /* writable */
/* hw knows */
select_wait (&(hwp->skel_wait_oq), wait);
return 0;
}
/* exception condition: cannot happen */
return 0;
}
As you can see, the kernel takes care of the hassle of managing wait queues, and you have only to check for readiness.
When we first wrote a select() call for a driver, we didn't understand the wait_queue implementation, and you don't need to either. You only have to know that the code works. wait_queues are challenging, and usually when you write a driver you have no time to accept the challenge.
Actually, select is better understood in its relationships with read and write: if select() says that the file is readable, the next read must not block (independently of O_NONBLOCK), and this means you have to tell the hardware to return data. The interrupt will collect data, and awaken the queue. If the user is selecting for writing, the situation is similar: the driver must tell if write() will block or not. If the buffer is full it will block, but you don't need to tell the hardware about it, since write() has already told it (when it filled the buffer). If the buffer is not full, the write won't block, so you return 1.
This way to think of selecting for write may appear strange, as there are times when you need to write synchronously, and you may expect that a device is writable when it has already accepted pending input. Unfortunately, this way of doing things will break the blocking/nonblocking machinery, and thus an extra call is provided: if you need to write synchronously, the driver must offer (within its fops) the fsync()call. The application invokes fops->fsync through the fsync() system call, and if the driver doesn't support it, -EINVAL is returned.
Imagine that you want to change the baud-rate of a serial multiport card you have built. Or tell your frame grabber to change the resolution of an image. Or whatever else... You could wrap these instructions into a series of escape sequences, such as, for example, the screen positioning in ANSI emulation. But, the normal method for this is to make an ioctl() call.
ioctl() calls as defined in <sys/ioctl.h> have the form
ioctl (int file_handle, int command, ...)
where ... is considered to be one argument of the type char * (according to the ioctl man page). Strange as it may be, the kernel receives these arguments in fs/ioctl.c in the form:
int sys_ioctl (unsigned int fd, unsigned int cmd,
unsigned long arg);
To add to the confusion, <linux/ioctl.h> gives detailed rules how the commands in the second parameter should be built, but nobody in all the drivers is actually following these ideas yet.
In any case, rather than cleaning up the Linux source tree, let's concentrate on the general idea of ioctl() calls. As the user, you pass the file handle and a command in the first two arguments and pass as the third parameter a pointer to a data structure the driver should read and/or write.
A few commands are interpreted by the kernel itself—for example, FIONBIO, which changes the blocking/non-blocking flag of the file. The rest is passed to our own, driver-specific ioctl() call, and arrives in the form:
int skel_ioctl (struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
Before we show a small example of a skel_ioctl() implementation, the commands you define should obey the following rules:
Pick up a free MAGIC number from /usr/src/linux/MAGIC and make this number the upper eight bits of the 16-bit command word.
Enumerate commands in the lower eight bits.
Why this? Imagine “Silly Billy” starts his favorite terminal program minicom to connect to his mailbox. “Silly Billy” accidentally changed the serial line minicom uses from /dev/ttyS0 to /dev/skel0 (he is quite silly). The next thing minicom does is initialize the “serial line” with an ioctl() using TCGETA as command. Unfortunately, your device driver, hidden behind /dev/skel0, uses that number to control the voltage for a long-term experiment in the lab...
If the upper eight bits in the commands for ioctl() differ from driver to driver, every ioctl() to an inappropriate device will result in an -EINVAL return, protecting us from extremely unexpected results.
Now, to finish this section, we will implement an ioctl() call reading or changing the timeout delay in our driver. If you want to use it, you have to introduce a new variable
unsigned long skel_timeout = SKEL_TIMEOUT;
right after the definition of SKEL_TIMEOUT and replace every later occurrence of SKEL_TIMEOUT with skel_timeout.
We choose the MAGIC '4' (the ASCII character 4) and define two commands:
# define SKEL_GET_TIMEOUT 0x3401 # define SKEL_SET_TIMEOUT 0x3402
In our user process, these lines will double the time-out value:
/* ... */
unsigned long timeout;
if (ioctl (skel_hd, SKEL_GET_TIMEOUT,
&timeout) < 0) {
/* an error occurred (Silly billy?) */
/* ... */
}
timeout *= 2;
if (ioctl (skel_hd, SKEL_SET_TIMEOUT,
&timeout) < 0) {
/* another error */
/* ... */
}
And in our driver, these lines will do the work:
int skel_ioctl (struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg) {
switch (cmd) {
case SKEL_GET_TIMEOUT:
put_user_long(skel_timeout, (long*) arg);
return 0;
case SKEL_SET_TIMEOUT:
skel_timeout = get_user_long((long*) arg);
return 0;
default:
return -EINVAL; /* for Silly Billy */
}
}
Georg V. Zezschwitz a 27-year-old Linuxer with a taste for the practical side of Computer Science and a tendency to avoid sleep.
XXXXXXXXXXXXXXXX (XXXXXXXXXXXXXX) Like Georg, is also 27-years-old and has the same interest in the practical side of Computer Science and the same tendency to avoid sleep.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- Designing Electronics with Linux
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- Build a Skype Server for Your Home Phone System
- Validate an E-Mail Address with PHP, the Right Way
- Why Python?
- A Topic for Discussion - Open Source Feature-Richness?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Great
3 hours 11 min ago - Reply to comment | Linux Journal
3 hours 19 min ago - Understanding the Linux Kernel
5 hours 34 min ago - General
8 hours 4 min ago - Kernel Problem
18 hours 7 min ago - BASH script to log IPs on public web server
22 hours 34 min ago - DynDNS
1 day 2 hours ago - Reply to comment | Linux Journal
1 day 2 hours ago - All the articles you talked
1 day 5 hours ago - All the articles you talked
1 day 5 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




Comments
Re: Kernel Korner: The Devil's in the Details
Hi
When you call wake_up_interruptible () from the handler the control unpends the sleeping task finish the read system call from the and then return back to continue the handler after calling wake_up ? Or else both the wake_up and unpending sleep task goes in parallel.