Monthly Archives: December 2015

Waiting / Blocking in Linux Driver Part – 3

<< Previous Article

The last article in this series focused on implementing the basic wait mechanism. It was a manual waiting where everything, starting from putting the process to the sleep to checking for the wake up event, was done by driver writer. But, such kind of manual waiting is error prone and may at times result in synchronization bugs. So, does kernel provide some robust wait mechanism? No points for guessing the right answer, yes it does. So, read on to explore more on wait mechanism in kernel.

Wait Queues

Wait queue is a mechanism provided in kernel to implement the wait. As the name itself suggests, wait queue is the list of processes waiting for an event. Below are the data structures for wait queues:

#include <linux/wait.h>
// Data structure: wait_queue_head_t
// Created statically 
DECLARE_WAIT_QUEUE_HEAD(wait_queue_name);
// Created dynamically
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);

As seen above, wait queues can be defined and initialized statically as well as dynamically. Once the wait queue is initialized, next step is to add our process to wait queue. Below are variants for this:

// APIs for Waiting
wait_event(queue, condition);
wait_event_interruptible(queue, condition);
wait_event_timeout(queue, condition, timeout);
wait_event_interruptible_timeout(queue, condition, timeout);

As seen, there are two variants – wait_event() and wait_event_timeout(). The former is used for waiting for an event as usual, but the latter can be used to wait for an event with timeout. Say, if the requirement is to wait for an event till 5 milliseconds, after which we need to timeout.

So, this was about the waiting, other part of the article is to wake up. For this, we have wake_up() family of APIs as shown below:

// Wakes up all the processes waiting on the queue
wake_up(wake_queue_head_t *);
// Wakes up only the processes performing the interruptible sleep
wake_up_interruptible(wait_queue_head_t *);

Below is modified code from the last article where we use wait queues:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/delay.h>

#define FIRST_MINOR 0
#define MINOR_CNT 1

static char flag = 'n';
static dev_t dev;
static struct cdev c_dev;
static struct class *cl;
static DECLARE_WAIT_QUEUE_HEAD(wq);

int open(struct inode *inode, struct file *filp)
{
	printk(KERN_INFO "Inside open\n");
	return 0;
}

int release(struct inode *inode, struct file *filp) 
{
	printk (KERN_INFO "Inside close\n");
	return 0;
}

ssize_t read(struct file *filp, char *buff, size_t count, loff_t *offp) 
{
	printk(KERN_INFO "Inside read\n");
	printk(KERN_INFO "Scheduling Out\n");
	wait_event_interruptible(wq, flag == 'y');
	flag = 'n';
	printk(KERN_INFO "Woken Up\n");
	return 0;
}

ssize_t write(struct file *filp, const char *buff, size_t count, loff_t *offp) 
{   
	printk(KERN_INFO "Inside write\n");
	if (copy_from_user(&flag, buff, 1))
	{
		return -EFAULT;
	}
	printk(KERN_INFO "%c", flag);
	wake_up_interruptible(&wq);
	return count;
}

struct file_operations pra_fops = {
	read:        read,
	write:       write,
	open:        open,
	release:     release
};

int wq_init (void)
{
	int ret;
	struct device *dev_ret;

	if ((ret = alloc_chrdev_region(&dev, FIRST_MINOR, MINOR_CNT, "SCD")) < 0)
	{
		return ret;
	}
	printk("Major Nr: %d\n", MAJOR(dev));

	cdev_init(&c_dev, &pra_fops);

	if ((ret = cdev_add(&c_dev, dev, MINOR_CNT)) < 0)
	{
		unregister_chrdev_region(dev, MINOR_CNT);
		return ret;
	}

	if (IS_ERR(cl = class_create(THIS_MODULE, "chardrv")))
	{
		cdev_del(&c_dev);
		unregister_chrdev_region(dev, MINOR_CNT);
		return PTR_ERR(cl);
	}
	if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, "mychar%d", 0)))
	{
		class_destroy(cl);
		cdev_del(&c_dev);
		unregister_chrdev_region(dev, MINOR_CNT);
		return PTR_ERR(dev_ret);
	}
	return 0;
}

void wq_cleanup(void)
{
	printk(KERN_INFO "Inside cleanup_module\n");
	device_destroy(cl, dev);
	class_destroy(cl);
	cdev_del(&c_dev);
	unregister_chrdev_region(dev, MINOR_CNT);
}

module_init(wq_init);
module_exit(wq_cleanup);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pradeep");
MODULE_DESCRIPTION("Waiting Process Demo");

As seen, the earlier manual waiting has been replaced by single statement wait_event_interruptible() which is more robust.

Below is the sample run of the above program, assuming that the module is compiled as wait.ko:

$ insmod wait.ko
Major Nr: 250
$ cat /dev/mychar0
Inside open
Inside read
Scheduling out

This gets our process blocked. Open another shell to wake up the process:

$ echo 'y' > /dev/mychar0
Inside open
Inside write
y
Inside close
Woken up
Inside close

As seen above, this will wake up the process, since the condition of flag being ‘y’ is satisfied.

Next Article >>

www.pdf24.org    Send article as PDF   

Playing Music using a Micro-controller

This 11th article in the series of “Do It Yourself: Electronics”, demonstrates playing various audio frequncies using timer module of a micro-controller.

<< Previous Article

After seeing the dimming power of the timer module of a micro-controller, Rohit was excited enough to experiment with the various other modes of the timer module. The idea of generating waveforms of various frequencies was generating all kind of random thoughts in his head. “Sound is also waves with some frequency – so can I generate sound using the timer?”, was one of the thoughts getting stronger and stronger. He finally decided to try it out.

As he has already tried with the Timer0, this time he thought of giving a try to another similar timer.

“Hey Pugs! Timer2 seems to be similar to Timer0. Can I try similar experiments with that as well?”, quizzed Rohit.

“Yes. Why not? Timer2 is also 8-bit with similar registers and modes”, replied Pugs.

“Also, the ATmega16 datasheet pg 122 mentions that the clear timer on compare match aka CTC mode gives more control to (compare match) output frequency.”

“Yes, it does. So, what are you trying?”

“Just thought of creating some sound.”

“Hmmm! That would be cool. Let’s give it a try.”

Unlike the earlier tried fast PWM mode, the timer counter clears to zero on match with value of OCR2 in CTC mode (WGM2[1:0] = 2), instead of hitting the maximum value. Also, by setting the COM2[1:0] to 1 the output on OC2/PD7 would keep on toggling on every match, thus generating a 50% duty cycle square wave. As per the ATmega16 datasheet pg 123, the waveform frequency would be given by

f = \frac{f_{cpu}}{2 . N . (1 + OCR2)}

where N represents the prescaler, to be set in the same register TCCR2, as the above settings.

“Based on the frequency formula, seems like so many ways to get the same frequency using the various prescaler values. How do we choose the prescaler?”, asked Rohit puzzled with the possibilities.

“Don’t get swayed by that. Note that both OCR2 and prescaler are integral and hence can’t generate all frequencies”, replied Pugs after a keen observation on the formula. “Also, the various N values would give the varying frequency resolutions. Hence, depending on our required resolution and may be range, we’d have to carefully choose the prescaler”, completed Pugs.

“That looks complicated and also interesting. Let me try a few combinations to get a feel of it.”

“Just keep in mind that for your sound generation to be audible, you’d need frequencies only between 20Hz to 20KHz.”

“Yes. Yes. I know that”, quipped Rohit.

Meanwhile, Pugs gave a shot to his shell scripting prowess, and ran the following shell command:

$ for ps in 1 8 32 64 128 256 1024
>do
>	echo -e "\nprescaler = $ps"
>	for ocr2 in `seq 0 255`
>	do
>		echo -n $((500000 / $ps / (1 + $ocr2)))
>		echo -n " "
>	done
>done

And from its output, he got well-interspersed frequencies in the audio frequency range for a prescaler of 8, as follows:

...
prescaler = 8
62500 31250 20833 15625 12500 10416 8928 7812 6944 6250 5681 5208 4807 4464 4166 3906
3676 3472 3289 3125 2976 2840 2717 2604 2500 2403 2314 2232 2155 2083 2016 1953 1893
1838 1785 1736 1689 1644 1602 1562 1524 1488 1453 1420 1388 1358 1329 1302 1275 1250
1225 1201 1179 1157 1136 1116 1096 1077 1059 1041 1024 1008 992 976 961 946 932 919
905 892 880 868 856 844 833 822 811 801 791 781 771 762 753 744 735 726 718 710 702
694 686 679 672 664 657 651 644 637 631 625 618 612 606 600 595 589 584 578 573 568
563 558 553 548 543 538 534 529 525 520 516 512 508 504 500 496 492 488 484 480 477
473 469 466 462 459 456 452 449 446 443 440 437 434 431 428 425 422 419 416 413 411
408 405 403 400 398 395 393 390 388 385 383 381 378 376 374 372 369 367 365 363 361
359 357 355 353 351 349 347 345 343 341 339 337 336 334 332 330 328 327 325 323 322
320 318 317 315 314 312 310 309 307 306 304 303 301 300 299 297 296 294 293 292 290
289 288 286 285 284 282 281 280 279 277 276 275 274 272 271 270 269 268 267 265 264
263 262 261 260 259 258 257 256 255 254 253 252 251 250 249 248 247 246 245 244
...

So, they decided to go ahead with the prescaler of 8, which translates to CS2[2:0] = 2, and this is what buzzer.c Rohit coded:

/*
 * This example uses CTC mode of 8-bit Timer/Counter2 for generating various
 * audio frequencies on OC2/PD7 (Pin 21).
 * Hence, Buzzer/Speaker is to be connected between OC2/PD7 (Pin 21) & GND.
 */

#include <avr/io.h>
#include <util/delay.h>

#define HUMAN_AUDIBLE_MAX_FREQ 20000

#define PRESCALER (2 << CS20) /* Prescaler N = 8 */
#define N 8
#define MAX_FREQ (F_CPU / (2 * N)) /* F_CPU / (2 * N * (1 + (OCR2 = 0))) */
#define MIN_FREQ (F_CPU / (2 * N * 256)) /* F_CPU / (2 * N * (1 + (OCR2 = 255))) */

void set_frequency(unsigned long freq)
{
	if (freq < MIN_FREQ)
	{
		freq = MIN_FREQ;
	} else if (freq > MAX_FREQ)
	{
		freq = MAX_FREQ;
	}
	/* f = MAX_FREQ / (1 + OCR2), i.e. OCR2 = MAX_FREQ / f - 1; */
	OCR2 = MAX_FREQ / freq - 1;
}
void init_timer(unsigned long freq)
{
	DDRD |= (1 << PD7); // OC2 is PD7 - setting it as output

	set_frequency(freq);

	/*
	 * Setting the Timer/Counter2 in CTC (Clear Timer on Compare) (non-PWM) mode.
	 * Toggling on Match to generate square wave for a particular frequency,
	 * as per the prescaler and OCR2 setting.
	 * Output would come on OC2/PD7 (Pin 21).
	 */
	TCCR2 = (1 << WGM21) | (0 << WGM20) | (1 << COM20) | PRESCALER;
}
void stop_timer(void)
{
	TCCR2 = 0;
}

int main(void)
{
	unsigned long freq = MIN_FREQ;

	init_timer(freq);

	while (1)
	{
		_delay_ms(100);
		freq += MIN_FREQ;
		if (freq > HUMAN_AUDIBLE_MAX_FREQ)
		{
			freq = MIN_FREQ;
		}
		set_frequency(freq);
	}

	stop_timer();

	return 0;
}

Then, Rohit compiled the program as follows (as in the previous articles):

$ avr-gcc -mmcu=atmega16 -DF_CPU=1000000 -Os buzzer.c -o buzzer.elf
$ avr-objcopy -O ihex buzzer.elf buzzer.hex

And finally, downloaded the buzzer.hex into the ATmega16 with J1 shorted (same as in the previous articles), using the following command:

$ avrdude -c ponyser -P /dev/ttyUSB0 -p m16 -U flash:w:buzzer.hex:i

And … removing the jumper J1, … nothing happened. Aha! forgot to connect the buzzer. Connecting the buzzer between PD7 (Pin 21) and GND, echoed the varying sound tones going from flat (high bass) to shrill (high treble), repeatedly.

Next Article >>

www.pdf24.org    Send article as PDF