Initial commit for blog migration to Hakyll

Never gave a damn about jekyll/ruby except to use it for github pages.
Now use Hakyll/Haskell and migrate from jekyll/ruby.
This commit is contained in:
Sanchayan Maity 2017-02-14 02:00:46 +05:30
commit 01d0d50249
56 changed files with 4764 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
_cache/
_site/
.stack-work/

14
about.markdown Normal file
View File

@ -0,0 +1,14 @@
---
title: About
---
Hello everyone!!
<p style='text-align: justify;'>I am Sanchayan Maity and I love electronics, programming, music and books. My other interests are Mathematics, Physics and Cooking for which I dont find time currently, but want to take them up in the long run. When it comes to sports I love cycling and badminton.</p>
<p style='text-align: justify;'>I use to live in Mumbai and work as an Embedded Systems Engineer at Godrej Security Solutions, a sub division of Godrej & Boyce Mfg. Co. Ltd. Now I live in Bangalore and work at Toradex as a Development Engineer where I work on Linux for our downstream BSP as well as make upstream contributions.</p>
<p style='text-align: justify;'>This blog is going to be mostly about the things which I learn and enjoy doing. There are two purposes of this blog. First is to serve as a personal journal of things I do, so that I can refer them later in case i forget. Second is to help other people, so that they do not have to struggle the same way I did.</p>
<p style='text-align: justify;'>“My philosophy, in essence, is the concept of man as a heroic being, with his own happiness as the moral purpose of his life, with productive achievement as his noblest activity, and reason as his only absolute.”
—Ayn Rand.</p>

11
contact.markdown Normal file
View File

@ -0,0 +1,11 @@
---
title: Contact
---
[Last.fm](http://www.last.fm/user/sanchayan_maity)
[Goodreads](http://www.goodreads.com/user/show/7006256-sanchayan-maity)
[Linkedin](https://in.linkedin.com/in/sanchayan-maity-57235953)
You can contact me on maitysanchayan@gmail.com

56
css/default.css Normal file
View File

@ -0,0 +1,56 @@
body {
color: black;
font-size: 16px;
margin: 0px auto 0px auto;
width: 600px;
}
div#header {
border-bottom: 2px solid black;
margin-bottom: 30px;
padding: 12px 0px 12px 0px;
}
div#logo a {
color: black;
float: left;
font-size: 18px;
font-weight: bold;
text-decoration: none;
}
div#header #navigation {
text-align: right;
}
div#header #navigation a {
color: black;
font-size: 18px;
font-weight: bold;
margin-left: 12px;
text-decoration: none;
text-transform: uppercase;
}
div#footer {
border-top: solid 2px black;
color: #555;
font-size: 12px;
margin-top: 30px;
padding: 12px 0px 12px 0px;
text-align: right;
}
h1 {
font-size: 24px;
}
h2 {
font-size: 20px;
}
div.info {
color: #555;
font-size: 14px;
font-style: italic;
}

11
hakyll-site.cabal Normal file
View File

@ -0,0 +1,11 @@
name: hakyll-site
version: 0.1.0.0
build-type: Simple
cabal-version: >= 1.10
executable site
main-is: site.hs
build-depends: base == 4.*
, hakyll == 4.9.*
ghc-options: -threaded
default-language: Haskell2010

BIN
images/haskell-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

16
index.html Normal file
View File

@ -0,0 +1,16 @@
---
title: Home
---
<h2>Welcome</h2>
<img src="/images/haskell-logo.png" style="float: right; margin: 10px;" />
<p>Welcome to my blog!</p>
<p>I've reproduced a list of recent posts here for your reading pleasure.</p>
<h2>Posts</h2>
$partial("templates/post-list.html")$
<p>…or you can find more in the <a href="/archive.html">archives</a>.</p>

View File

@ -0,0 +1,38 @@
---
author: Sanchayan Maity
title: CGI and SSI with lwip on Stellaris
tags: CGI, SSI, lwip, Stellaris
---
<p style='text-align: justify;'>CGI stands for Common Gateway Interface and SSI stands for Server Side Include. The below post is going to be about, how to use them with lwip for employing web page interface in your embedded system.</p>
<p style='text-align: justify;'>First, i would like to give some clarification. My work on the above was build up over and above the enet_io example code provided by Texas Instruments with their Stellaris LM3S6965 boards. My main aim of this post is to help you in understanding the code flow and is very specific to the example provided. I have not done any work in porting lwip nor written this application from ground up.</p>
So let's get started.
<p style='text-align: justify;'>You will need to have the html code and the program code side by side to understand what's going on.</p>
<p style='text-align: justify;'>With web pages, you would like to display some data and send data entered on the web page to your embedded system.</p>
1. SSI Server Side Includes (Displaying data on web page)
<p style='text-align: justify;'>When a web page request is sent from a browser to your embedded system, which has got lwip configured with CGI and SSI, if any SSI tag is present on the web page being served, the SSI handler is called. These handlers are registered with a call to the function http_set_ssi_handler(SSIHandler, g_pcConfigSSITags, NUM_CONFIG_SSI_TAGS), where SSIHandler is the function which gets called, when the web page is served containing SSI tags. g_pcConfigSSITags is an array containing the list of SSI tags. These names you will find in the html code of the web page and in the program code file  enet_io.c.  NUM_CONFIG_SSI_TAGS is the number of individual SSI tags, the http server can expect to find.</p>
<p style='text-align: justify;'>To understand what i said, let me take you through the enet_io code. Open the folder fs in the code directory, and open the htm web page with a web brower.  In the web page, the SSI tag corresponding to this, can be found in I/O Control Demo2 link. Use view source, by right clicking on the web page. The SSI tag will be like <!--#PWMfreq-->. In the g_pcConfigSSITags array, you will see "PWMfreq". So, whenever the system is serving this web page, as there is an occurrence of an SSI tag, the SSI handler is called, and the value of PWMfreq is substituted by the actual value. In the SSI handler, you will find something like below,</p>
case SSI_INDEX_PWMFREQ:
ulVal = io_get_pwmfreq();
usnprintf(pcInsert, iInsertLen, "%d", ulVal);
break;
<p style='text-align: justify;'>When the user requests the web page, on seeing the occurence of the SSI tag, the SSI handler gets called, the case corresponding to PWMFreq, is selected and executed, the io_get_pwmfreq() returns the value of pwm frequency on the system, and this is inserted in the web page by usnprintf function. This gets done for each SSI tag in the web page. In case, you have defined a tag on the web page, but, not written a corresponding entry in the tags array and the handler, you will get an error on the web page, complaining no tag found.</p>
<p style='text-align: justify;'>To summarise, register your SSI handler, include the SSI tag in the tags array, include a corresponding entry in the handler, which will return its value. And viola!. You can now display values on web pages.</p>
2. CGI Common Gateway Interface (Sending data to the system)
<p style='text-align: justify;'>Lets say you enter a new value of PWM frequency and click on Update Settings. On clicking, just check what url is displayed on the address bar of your browser. I entered a value of 50 for PWM frequency and the url changed to "file:///C:/StellarisWare/boards/ek-lm3s6965/enet_io/fs/iocontrol.cgi?LEDOn=1&PWMFrequency=50&PWMDutyCycle=100&Update=Update+Settings". ( N.B. I was not connected to the board while writing this and executed it from my local drive, so the file url). You can see, how the value of 50 was appended and send to the system. Also, notice the iocontrol.cgi in the url. This is defined along with the corresponding CGI handler in the g_psConfigCGIURIs array. When your board, receives this web page request, the lwip stack knows which function to call. The handlers are registered with a call to http_set_cgi_handlers(g_psConfigCGIURIs, NUM_CONFIG_CGI_URIS).  In the corresponding CGI handler, the parameters are extracted from the URL, by FindCGIParameter function and GetCGIParam function, and the functions are called for setting these values.</p>
<p style='text-align: justify;'>This is how data gets passed to the system using CGI through web pages.</p>
<p style='text-align: justify;'>I hope, I was able to at least clear a little bit for you to start off on your own and make changes you want. In case, i missed something, do post a comment and I will try to help you out.</p>

View File

@ -0,0 +1,23 @@
---
author: Sanchayan Maity
title: Debugging a hard fault in ARM Cortex M3
tags: ARM Cortex M3, Hard Fault, Stellaris
---
<p style='text-align: justify;'>I have been working on the Stellaris LM3S6965 for quiet a while now. So i thought, may be i will put my learning's in one place, so others can use them and not get stuck, where i did.</p>
<p style='text-align: justify;'>The Stellaris LM3S6965 is based on the ARM Cortex M3 architecture. What i am going to mention in my post, is how to track down the source of a hard fault. So let's get straight to the point.</p>
<p style='text-align: justify;'>On getting a hard fault, check the value of MSP and PSP viz. the stack pointers. It's likely the MSP, if you are not using an RTOS.</p>
<p style='text-align: justify;'>Go to the address contained in the MSP. The following registers would be on the stack in increasing order.</p>
Registers ro - r3, r12, lr, pc, xpsr.
<p style='text-align: justify;'>One thing i must mention is, if your fault handler does something other than spinning in a loop, a couple of other registers might be pushed on to the stack by the compiler. You need to take care of this. I am assuming here, that the default fault handler does nothing else besides spinning in a loop.</p>
<p style='text-align: justify;'>So take a look at the stack. The pushed PC should help you in debugging, as it usually points to the instruction that caused the fault.</p>
<p style='text-align: justify;'>The next place to look is in the NVIC viz. the Nested Vector Interrupt Controller. The Configurable Fault Status Register is at address 0xE000ED28 and contains bits that identify the cause of the fault and can also tell if the stacked PC actually points to the faulting instruction. There are a couple of other registers, the Memory Manage Fault Address register and the Bus Fault Address register which provide the address of the fault for those kinds of faults. But, you need to look at the status register first to identify the cause of the fault and that will tell you if the BFAR or MMFAR contains a useful address.</p>
<p style='text-align: justify;'>To learn more, refer to the ARM Cortex M3 technical reference manual. If you would like to learn about ARM architecture in general, the ARM System Developer's Guide by Andrew Sloss, Dominic Symes and Chris Wright is a very good place to start.</p>

View File

@ -0,0 +1,102 @@
---
author: Sanchayan Maity
title: TCP echo server with lwip
tags: lwip
---
<p style='text-align: justify;'>LWIP stands for Lightweight Internet Protocol stack and was developed by Adum Dunkels at the Swedish Institute Of Computer Science. It is open source TCP/IP stack designed mainly for embedded systems and written in C.</p>
<p style='text-align: justify;'>Today, i will give you guys a sample code for implementing TCP/IP echo server. The assumption is that, you have lwip already ported to your controller. So, here is the code.</p>
{% highlight c %}
char tcp_buffer[1024];
static void close_conn (struct tcp_pcb *pcb )
{
tcp_arg(pcb, NULL);
tcp_sent(pcb, NULL);
tcp_recv(pcb, NULL);
tcp_close(pcb);
}
static err_t echo_recv( void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err )
{
int i;
int len;
char *pc;
if ( err == ERR_OK && p != NULL )
{
tcp_recved( pcb, p->tot_len );
pc = (char *)p->payload;
len =p->tot_len;
for( i=0; i<len; i++ )
{
tcp_buffer[i] = pc[i];
}
if( tcp_buffer[0] == 'X' )
close_conn( pcb );
pbuf_free( p );
if( len > tcp_sndbuf( pcb ) )
len= tcp_sndbuf( pcb );
tcp_write( pcb, tcp_buffer, len, 0 );
tcp_sent( pcb, NULL );
}
else
{
pbuf_free( p );
}
if( err == ERR_OK && p == NULL )
  {
close_conn( pcb );
}
return ERR_OK;
}
static err_t echo_accept(void *arg, struct tcp_pcb *pcb, err_t err )
{
LWIP_UNUSED_ARG( arg );
LWIP_UNUSED_ARG( err );
tcp_setprio( pcb, TCP_PRIO_MIN );
tcp_recv( pcb, echo_recv );
tcp_err( pcb, NULL );
tcp_poll( pcb, NULL, 4 );
return ERR_OK;
}
void tcp_init( void )
{
struct tcp_pcb *tcp_pcb;
tcp_pcb = tcp_new();
tcp_bind(tcp_pcb, IP_ADDR_ANY, 23);
tcp_pcb = tcp_listen( tcp_pcb );
tcp_accept( tcp_pcb, echo_accept );
}
{% endhighlight %}
<p style='text-align: justify;'>Instructions for use:</p>
<p style='text-align: justify;'>1. Set up lwip stack with a call to lwipInit function.</p>
<p style='text-align: justify;'>2. Call tcp_init() before entering while(1). Everything will be running in an interrupt context, so if you want to test this application alone, your while(1) can be empty.</p>
<p style='text-align: justify;'>Let me explain how the code works.</p>
<p style='text-align: justify;'>First, on call to the tcp_init function, a pointer to tcp_pcb structure is created. pcb is the short form of Protocol Control Buffer. Then a new tcp_pcb structure is created, with a call to tcp_new function, which returns a pointer, which is assingned to the pointer we just declared. This is then bound to the pcb structure, by call to tcp_bind and passing the pointer to tcp_pcb structure as the first argument, binding it to any ip address, which the board is assigned or gets through dhcp, as the second argument and finally the tcp port number on which communication will take place as the third argument. A call to tcp_listen puts the connection in a listening state for any incoming connections. The tcp_accept function is passed the pointer we have been using till now, and the name of the function as the second argument, which will be called, when an incoming connection request arrives on the listening connection. This is an implementation of callback functions. Google it. Here is a link [http://www.newty.de/fpt/callback.html](http://www.newty.de/fpt/callback.html).</p>
<p style='text-align: justify;'>When an incoming connection request arrives, the echo_accept function is called. The main line here is the fourth line. It passes the relevant pointer as the first argument and the function to be called when new data arrives.</p>
<p style='text-align: justify;'>On data arrival, the echo_recv function gets called. tcp_recved must be called when the application has received the data. The len argument indicates the length of the received data. After this, the data is taken (should be self explanatory on how the data is extracted and what happens when an "X" is sent) and send back with a call to tcp_write and tcp_sent.</p>
<p style='text-align: justify;'>In case, you are using it with Stellaris, to which it has already been ported to by Texas Instruments, go the StellarisWare folder. In it, go to third_party, lwip-1.3.1 (your lwip version may be different) and then docs. Open and read the rawapi document in it, to understand the use of these functions.</p>
Hope, that helps.
<p style='text-align: justify;'>**N.B.** Every function in lwip returns error codes. I have ignored the return codes in most of the cases above. To make a more robust application, perform your operation based on what return codes you get.</p>

View File

@ -0,0 +1,88 @@
---
author: Sanchayan Maity
title: OpenCV on Beagleboard-xM | Getting started
tags: beagleboard, facedetection, opencv
---
Before I start, let me mention a few things which I am assuming.
1. You know how to use the vi Editor.
2. You are connected over a serial link to the BB - xM and are doing all operations from the terminal.
<p style='text-align: justify;'>Get the Angstrom Image from the below link and set up the SD card as per  the instructions given.</p>
[http://elinux.org/Getting_a_Workshop_SD_Image](http://elinux.org/Getting_a_Workshop_SD_Image)
<p style='text-align: justify;'>After following the set up procedure, you will have a boot partition and root filesystem partition on the SD card.</p>
<p style='text-align: justify;'>After setting up the SD card, transfer an image in which you want faces to be detected. This image you can transfer to the /home/root directory in the root filesystem on the SD card.</p>
<p style='text-align: justify;'>Plug the SD card and boot up. The board will boot up and you will get the login prompt. Log in using root as the password, after which, you will be placed in the /home/root directory. This is the reason i told you to copy the image in this particular directory.</p>
<p style='text-align: justify;'>Use vi and open up a file faces.c by typing "vi faces.c" on the command line. Write the below code in it.</p>
{% highlight c %}
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include "stdio.h"
#include "string.h"
int main(void)
{
const char * cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml";
IplImage * img;
CvHaarClassifierCascade * hc;
CvSeq * faces;
CvMemStorage * storage = cvCreateMemStorage(0);
CvSize minSize = cvSize(20, 20);
CvSize maxSize = cvSize(60, 60);
img = cvLoadImage("faces.jpg",0);  //Assuming faces is in the directory the code is in.
if(img)
{
printf("Success");
printf("\n");
}
else
{
printf("Failed");
printf("\n");
}
hc = (CvHaarClassifierCascade*)cvLoad(cascade_path,NULL,NULL,NULL);
faces = cvHaarDetectObjects(img, hc, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, minSize, maxSize);
if(faces)
{
printf("faces detected %d\n",faces->total);
}
else
{
printf("cvHaarDetectObjects returned null\n");
}
cvReleaseHaarClassifierCascade( &hc );
cvReleaseImage( &img );
cvReleaseMemStorage( &storage );
return 0;
}{% endhighlight %}
After saving the above program, compile it using the command:
`arm-angstrom-linux-gnueabi-gcc   faces.c   /usr/lib/libopencv_*.so   -o   faces`
<p style='text-align: justify;'>I have mentioned /usr/lib/libopencv_*.so on the command line, as the compiler would then dynamically link all the OpenCV library files against this code.</p>
<p style='text-align: justify;'>This will create a executable called faces. Run it from the command line by typing "./faces". The code would run and print the number of faces detected in the image on the command line.</p>
<p style='text-align: justify;'>I didn't have a HDMI to DVI-D cable to connect my monitor to the board, so i printed the output on the command line. If you can connect a monitor, use the functions from the highgui library, and you can display your output on the monitor. You can draw rectangles around the faces detected.</p>
<p style='text-align: justify;'>I am currently working on face detection in video and facing a few glitches, but, i will post it when i am successful.</p>
<p style='text-align: justify;'>To learn what the functions actually do, refer to the OpenCV 2.2 C reference on the below link.</p>
[http://opencv.jp/opencv-2.2_org/c/](http://http://opencv.jp/opencv-2.2_org/c/).
<p style='text-align: justify;'>**Disclaimer: I have modified the example code given in the reference documentation to suit my own needs.**</p>

View File

@ -0,0 +1,15 @@
---
author: Sanchayan Maity
title: GDB Tutorial | Debugging with GNU GDB Debugger
tags: debugging, gdb
---
<p style='text-align: justify;'>Only a developer can understand the importance of a debugger. When you are working on a program and things don't perform as expected, the debugger can help one locate the source of the problem. Tools like JTAG, GDB or network debugging tools like Wireshark can become one's best friend.</p>
<p style='text-align: justify;'>I have used JTAG and Wireshark extensively, but, hadn't use GDB till date. So, i was searching for some tutorials on GDB and i found an absolutely brilliant tutorial on it. Though the below tutorial teaches you how to use GDB, the best part of the article had to be "Debugging with your brain".</p>
[GDB Tutorial](http://www.dirac.org/linux/gdb/)
Go read it as soon as you can.
Thanks to Peter Jay Salzman for this tutorial.

View File

@ -0,0 +1,12 @@
---
author: Sanchayan Maity
title: What you should know about memory
tags: memory
---
<p style='text-align: justify;'>I don't remember how I stumbled across the article to which I am going to provide
you the link, but, it's a damn good article written by Mr. Ulrich Drepper on the lwn site. Anyone wanting to know about memory and how you can optimize your code to use this memory, should read it. I still haven't finished reading it completely, but, on my way there.</p>
Here's the link below
[http://lwn.net/Articles/250967/](http://lwn.net/Articles/250967/)

View File

@ -0,0 +1,105 @@
---
author: Sanchayan Maity
title: Best books for Electronics Engineering
tags: books
---
<p style='text-align: justify;'>In this blog post, i am going to mention some of the best books, i have come across for learning the basic core subjects in Electronics. So, without wasting any more time, here is the list. A disclaimer first though.</p>
**Disclaimer**:
<p style='text-align: justify;'>1. If you are looking to just pass the exams, then this post is not for you. Read your useless notes and local author books. The below books are for knowledge seekers, not for dumb future managers. The below post is not for you. It's for those, who want to learn for the sake of learning, for those who find it fun and like to know how things work.</p>
<p style='text-align: justify;'>2.I have not read all the books completely from start to end, but, i have tried to read most of it. During my college days, i simply could not manage to prepare for exams properly, while, also learning through those books. I have started though, to read the books, one by one and have kept myself an aim of reading each of them completely.</p>
<p style='text-align: justify;'>If you live in India and can't find those books at your local bookshop, use flipkart for getting them.</p>
**1. Mathematics**
<p style='text-align: justify;'>How can anyone hope to learn anything in the scientific and engineering field without Mathematics? Yet, there are fools who say what's the point of learning it, when one is not probably ever going to use anything more than simple Algebra. Well, if you are learning engineering for the sake of earning money, then you are in the wrong place.</p>
Advanced Engineering Mathematics by Erwin Kreyszig
<p style='text-align: justify;'>This has got to be one of the best books for Engineering Mathematics. Covers proofs, good practical sums and gives a solid understanding.</p>
**2. Digital Design**
Digital Design by John Wakerley
Digital Design by Morris Mano
Both are pretty good books. The one by John Wakerley also covers HDL languages.
**3. Electrical Network Analysis**
Engineering Circuit Analysis by William Hayt and Jack Kemmerly
**4. Control Systems**
Modern Control Engineering by Katsuhiko Ogata
Automatic Control Systems by Benjamin Kuo
**5. Electronic Circuit Analysis and Design**
Electronic Circuit Analysis and Design by Donald Neamen
Integrated Electronics by Jacob Millman and Christos Halkias
The Art Of Electronics by Paul Horowitz
<p style='text-align: justify;'>The first one is damn good. The second one is a bit dated, but, covers the concepts quiet well. For example, it does not cover MOSFET's thoroughly, while covering JFET's, but, covers and teaches low and high frequency analysis of transistors like a breeze. The third one does not contain much maths, but, teaches practical design concepts pretty clearly. After reading CE amplifier and emitter follower design from it, you will feel it was so simple all the while. In college and classes, they just give you steps, without explaining any of the concepts behind it. Use the third in conjunction with the first or second.</p>
<p style='text-align: justify;'>If you really love analog, go for Analysis and Design of Analog Integrated Circuits by Paul Grey, Paul Hurst, Stephen Lewis and Robert Meyer.</p>
<p style='text-align: justify;'>I am going to express my rants again. I wish, i had never referred the local author J S Katre's book. His books are a piece of shit. During my first two years of engineering, i felt disappointed and lost. Got carried away by the senior's and peer talks of reference and foreign author books being difficult to read. It's the exact opposite. The books are so good and they explain things properly. I had this mental block in my head and found Neamen's book pretty tough to read, when i first read it. Never touched it again, till, after the end of fourth semester. I had failed in Digital Design during third semester, not having studied anything. As an experiment, i bought Digital Design by John Wakerley and read it. I did it just for the heck of it. I realized, that it was simple and easy to read from those books. So simple and precisely clear. The topic on logic families was handled so effortlessly in that. After the end of fourth semester, in vacations, i took up the book by Donald Neamen. At once, i realized my mistake. So, if you are reading this, and like to learn, refer good books. Don't listen to your seniors and peers, who will tell you otherwise. They are losers. Go get good books.</p>
**6. Communications**
Modern Analog and Digital Communication by B. P. Lathi
<p style='text-align: justify;'>This has got to be one of the best books i have ever read. He explains concepts through mathematics as well as simple intuitive explanations. For the first time, this book made me realize, why having a good grasp of a language is essential. The English is plain and simple, yet, precise and clear. His full name is Bhagvandas Pannalal Lathi. Did his Bachelors from a Pune University and Master's from Oxford. I never expected such a good book from an Indian author. His book is an example of how all beginner to intermediate level texts should be written. The book covers Analog and Digital Communication thoroughly well. Also, this book makes it quiet clear of why Signals and Systems should be covered first, before teaching analog communication concepts and why Probability and Random Processes should be taught before teaching Digital Communication. Fucking Mumbai University teaches Analog communication first, before Signals and Systems and does not teach Probability and Random Processes before Digital Communication. Fucking idiots!!.</p>
**7. Signals and Systems**
Signal Processing and Linear Systems by B. P. Lathi
Signals and Systems by Alan V. Oppenheim
<p style='text-align: justify;'>The first one is again one of the best books ever. The way anything should be taught and explained. How many people can tell the difference between Analog, Continuous, Digital and Discrete signals. Each one is different!. Second one is good too and uses a proper mathematical treatment. You can also refer Discrete Time Signal Processing by Alan V. Oppenheim and Digital Signal Processing by John Proakis for going further.</p>
**8. Electromagnetics**
Engineering Electromagnetics by William Hayt
Electromagnetic Waves and Radiating Systems by Edward Jordan and Keith Balmain
Elements of Electromagnetics by Mattew Sadiku
<p style='text-align: justify;'>The first and the third are good beginner to intermediate level texts. The first is especially good. The second is a fairly advanced text, but, contains a wealth of knowledge. I hope to finish and thoroughly understand this book some day.</p>
**9. Microwaves**
Microwave Engineering by David Pozar
<p style='text-align: justify;'>Very good book on the subject. I had the one by Samuel Liao, but, that sucks. The one by local author Wali (one used fairly widely in Mumbai) is a cheap copy without any coverage of concepts.</p>
**10. Power Electronics**
Power Electronics by Ned Mohan, Undeland and Robbins
Fundamentals Of Power Electronics by Robert Erickson and Dragan Maksimovic
<p style='text-align: justify;'>The first one is good for clearing all basics and the second one is quiet good, if you want to know about switching converters. The book Power Electronics by Khanchandani, is a piece of crap. Never understood a thing from it.</p>
**11. VLSI Design**
Digital Integrated Circuits A Design Perspective by Jan Rabaey
<p style='text-align: justify;'>This one is a very good book for VLSI. There is another book by Kang which is also a must have. The one by the local author Irfan, which is widely used around in Mumbai, is a one to one copy of Kang. The Tata McGraw Hill company should sue him for plagiarizing such a good work by Kang.</p>
**12. Filter Design**
<p style='text-align: justify;'>Sorry, haven't come across a good book myself for this subject. Have been looking through various suggestions on the net. Will post when i get around to reading one.</p>
<p style='text-align: justify;'>hese are some of the best books for electronics, i have come across. The list is by no means exhaustive and there may be a lot of other good books for the subjects.</p>

View File

@ -0,0 +1,49 @@
---
author: Sanchayan Maity
title: Learning the importance of PCB Design - My experience
tags: hardware
---
<p style='text-align: justify;'>I don't know about the education system of other countries, but, in India, it's funny how most engineering colleges never bother to teach concepts and application of the concepts to practical real world problems. Though it very well may be the same in other countries as well, from what i have been reading about the state of education, even in countries like the US.</p>
<p style='text-align: justify;'>During my engineering college days, one of the most difficult subject for me to understand was Electromagnetics. I never thought that, one day, it would be the subject that would interest me the most and i would come across it's application in such a way, as i am soon going to tell you about.</p>
<p style='text-align: justify;'>I was working on a system which used a 10/100 Mbps Ethernet connection for communication with the external world and for exchanging data. I designed the system, it was not a tough one, but, it had this one high speed interface. My understanding prior to first encountering failure on this project, was that on a Printed Circuit Board, a connection between Point A and Point B, is just a simple copper track laid out on the board. I made the schematics and send them to our local third party vendor. The vendor was just a regular run of the mill local guy, who didn't have any idea about such things, well neither did any of my seniors had any idea nor I. That vendor just tried to do the routing using the auto routing facility available in most PCB Design software these days like the PADS tool by Mentor Graphics. If that didn't work out properly, he would do the routing himself, trying to fit the tracks on the PCB properly.</p>
<p style='text-align: justify;'>So, the first sample prototypes came. I started testing them and as it turned out, the Ethernet interface didn't work. I started doing research on the net, as to what could be the problem. And alas!! I find out about PCB design and it's importance. All the PCB's in the world with high speed interfaces, would have been impossible without PCB design. And as it turns out, it happens to be an application of the principles of Electromagnetics. Now, enough of my background experience, let me tell you why it's important.</p>
<p style='text-align: justify;'>At low speeds the tracks on a PCB act just as normal electrical point to point connections. At high speeds, high frequency effects come into play. Remember lumped parameters and distributed parameters?. At low frequencies, the wavelength of the signal being propagated from one point to other is greater than the length of the track. At high frequencies, the wavelength of the signal is less than the length of the track. The lumped parameter model is considered for the former, while, the distributed parameter model is considered for the latter. Basically, parasitic inductance and capacitance come into play, impedance matching becomes important, length of tracks become important and these effects are just to name a few.</p>
<p style='text-align: justify;'>Consider, you are sending a high frequency signal from Point A to Point B. Now, at high frequencies, these tracks start acting as transmission lines. So, if  impedance of the track viz. the transmission line is not matched to the load (the point of reception), a part of the signal is transmitted and part of it is reflected. Basic transmission line theory, if Zl != Zo, you get a reflection co-efficient, where Zl is the load impedance and Zo is the characteristic impedance of the transmission line. The reflected signal interferes with the transmitted signal, and depending on the impedance mismatch, the signal can degrade significantly. In the case of Ethernet, a differential impedance of 50 ohms has to be maintained. Also, in case of a differential pair, the'+' and '-' tracks should be of equal length. To maintain this impedance, the width of the track and the distance of the track from the ground layer has to be set properly. The (W/H) ratio is important, where W is the width of the track and H is the height of the track viz, the layer the track is running on to the ground plane layer, assuming you have a seperate ground layer available.</p>
<p style='text-align: justify;'>So, impedance matching is important and one of the parameters to take care of in PCB design. Other important factors to take care of include crosstalk, reducing any spurious EMI emissions, proper clock distribution, proper via placement and sizing, taking care of current return paths and layerstacking.</p>
<p style='text-align: justify;'>The above was just a very short explanation of why PCB design is important and an account of my own experience. I will try to cover the topics mentioned above in my future blog posts. In the mean while, if you are like me, who likes to explore things, by reading books and online content doesn't and never will suffice, below, is a list of books you can refer to for delving deeper into the subject.</p>
<p style='text-align: justify;'>If you are good in Electromagnetics or have a decent understanding of it, skip the first two levels.</p>
Level I. Basic Electromagnetics
<p style='text-align: justify;'>1. Engineering Electromagnetics by William Hayt (Good for beginners)</p>
<p style='text-align: justify;'>2. Electromagnetics Waves and Radiating Systems by Edward C. Jordan and Keith G. Balmain (Much advanced compared to the first, but, contains a wealth of information. Hats off to you, if you can digest everything in this book)</p>
Level II. Microwaves
Microwave Engineering by David Pozar
Level III. PCB Design
1. High Speed Digital Design - A Handbook Of Black Magic by Howard Johnson and Martin Graham
2. Advanced Signal Integrity for High Speed Digital Designs by Stephen Hall and Howard Heck
<p style='text-align: justify;'>The first one is for beginners and doesn't require high level math or calculus. A must read for all embedded engineers. The second one is detailed and dives into PCB design from a mathematical and detailed theorotical point of view and requires knowledge of vectors and calculus.</p>
<p style='text-align: justify;'>An advanced book is available for the first one, but, no low price edition available in India. Another book by Eric Bogatin is also available, but, again no low price edition available in India. I mentioned the second book, because, i managed to get hold of a PDF for it. Now, i don't want to support piracy, but, since i couldn't lay my hands on any Signal Integrity book for under 5K, i had no other choice. That book is very good and a must read though. All specialised books happen to be so expensive or difficult to find in India. If any of the authors or book publishers come across this post, a sincere request to you, please make a low price edition available. Paying 5 grand for a book is a bit too much, though, i can afford it!</p>
Blogs
[http://www.sigcon.com/Pubs/pubsAlpha.htm](http://www.sigcon.com/Pubs/pubsAlpha.htm)
[http://bethesignal.net/blog/](http://bethesignal.net/blog/)

View File

@ -0,0 +1,25 @@
---
author: Sanchayan Maity
title: A little something on Linux Device Drivers
tags: device drivers, linux
---
<p style='text-align: justify;'>I have been struggling for a while now, trying to learn device drivers and system programming in Linux. One of the many questions i had was, how does the CPU using the Linux kernel know, which devices are present and what are the drivers for them. The answer to this question is through "Platform Device and Platform Data". Though soon enough, you may find them obsolete as Linux very well might completely start using Device Trees. Below, are a few links from which you can learn about the Platform Device API and Device Tree.</p>
[http://lwn.net/Articles/448499/](http://lwn.net/Articles/448499/)
[http://lxr.free-electrons.com/source/Documentation/driver-model/](http://lxr.free-electrons.com/source/Documentation/driver-model/)
[http://www.devicetree.org/Device_Tree_Usage](http://www.devicetree.org/Device_Tree_Usage)
<p style='text-align: justify;'>You will find extra information at </p>
[http://lxr.free-electrons.com/source/Documentation/](http://lxr.free-electrons.com/source/Documentation/)
<p style='text-align: justify;'>under various sub divisions. For example, check out </p>
[http://lxr.free-electrons.com/source/Documentation/i2c/writing-clients](http://lxr.free-electrons.com/source/Documentation/i2c/writing-clients)
and
[http://lxr.free-electrons.com/source/Documentation/i2c/instantiating-devices](http://lxr.free-electrons.com/source/Documentation/i2c/instantiating-devices)

View File

@ -0,0 +1,41 @@
---
author: Sanchayan Maity
title: What is MLO file?
tags: MLO, beagleboard
---
<p style='text-align: justify;'>I have had the Beagle-xM for a while now. If you have worked on it, you must have noticed the MLO file, which is required to be present in the bootpartition of the Micro SD card. The file is necessary for booting the system. So ever wondered, what is this MLO file? No? What are you doing with the Beagle-xM anyways? Yes? Well, then read on.</p>
<p style='text-align: justify;'>As far as i know, all TI SOC's like DM368 and DM3730 (on which i have worked) have a ROM bootloader. On sytem start up or reset, the ROM bootloader(RBL) starts running. The job of this RBL, is to do some very bare minimum initialisation and then find and boot from a device such as MMC card or flash. The RBL has got the capability to boot from a variety of devices like NOR flash, NAND flash, MMC, USB or UART. To know everything in detail,read the 26th chapter of the DM3730 Technical Reference Manual having the TI document number SPRUGN4N.</p>
<p style='text-align: justify;'>Now, how does the chip know which device to boot from you ask?. The chip has got a set of pins, sys_boot[4:0] pins, whose state decides what memoryor peripheral to boot from. Also, there is a predefined booting order in the case of both memory booting or peripheral booting. For example, let's say the state of sys_boot[4:0] pins is 0b01110. Then, the booting order will be XIPwait, DOC, USB, UART3 and finally MMC1. XIPwait is Execute in Place memory with wait monitoring and DOC is DiskOnChip memory. You can find this information on Page 3549 of the DM3730 TRM.</p>
<p style='text-align: justify;'>In the case of Beagle-xM, the state of the sys_boot pins cause it to boot from the MMC card at the start. As a result, on power on, the ROM will initialise the SD/MMC device, detect a card and look for a boot image. The ROM is capable of reading from the card with a filesystem. It will search for a file named MLO which should be located on the boot partition, on a partition of type FAT16/32. So, this explains the name MLO.</p>
<p style='text-align: justify;'>Now, let's talk about the flow of control. The sys_boot pins specify where the first external boot loader MLO can be found. After the MLO is located and executed, it passes the control to the second external boot loader viz. uboot. Have you seen the uboot.bin file in the boot partition of the micro SD card? No? Jeez, have you been sleeping? U-boot is the application which passes control to the Linux system. The main goal of u-boot is to retrieve the Linux kernel and provide the kernel with information about the location of the Linux filesystem. U-boot can be configured to retrieve the kernel from a variety of options like NFS or flash. U-boot then boots the Linux kernel. The Linux kernel mounts the Linux root filesystem.</p>
<p style='text-align: justify;'>In case, you are interested further in knowing the boot sequence, below is a link, which describes the boot sequence.</p>
[http://blog.techveda.org/](http://blog.techveda.org/)
<p style='text-align: justify;'>It uses AT91RM9200 as the reference, but, still should be very informative enough. Also, you can use this link to browse the source.</p>
[http://lxr.free-electrons.com/](http://lxr.free-electrons.com/)
<p style='text-align: justify;'>Hope you found this "What is MLO file?" post informative.</p>
<p style='text-align: justify;'>**Addendum:** Mikkel Nielsen shared quite a few important links and had this to quote. You can also find this in the comments.</p>
<p style='text-align: justify;'>Think it could also be Memory LOader, as it is a file containing (apart from metadata) binary code that is loaded directly into internel SoC memory.</p>
<p style='text-align: justify;'>TI also refers to it as X-loader</p>
[http://processors.wiki.ti.com/index.php/Boot_Sequence](http://processors.wiki.ti.com/index.php/Boot_Sequence)
<p style='text-align: justify;'>This suggests it is short for Memory LOcator.</p>
[https://groups.google.com/forum/#!msg/beagleboard/M-ew_WGjE_A/W2OerOFV3yYJ](https://groups.google.com/forum/#%21msg/beagleboard/M-ew_WGjE_A/W2OerOFV3yYJ)
<p style='text-align: justify;'>Some history about why it is like that here.</p>
[http://lists.denx.de/pipermail/u-boot/2013-December/168481.html](http://lists.denx.de/pipermail/u-boot/2013-December/168481.html)

View File

@ -0,0 +1,17 @@
---
author: Sanchayan Maity
title: Arch Linux - Finally!!
tags: arch linux
---
<p style='text-align: justify;'>I have tried Ubuntu 10.04, Ubuntu 11.10, Ubuntu 12.04, Debian Squeeze, Mint Maya and Fedora. Of these, I have enjoyed Mint Maya the most. I got pissed with the Unity interface and though Mint was super nice, I wanted something more to fiddle with. Having heard of Arch and Gentoo being the two distributions which are toughest to install, but, customizable exactly the way one wants it, I decided to try one of these.</p>
<p style='text-align: justify;'>I surfed the net for a while trying to find which one will suit my need. Gentoo was super customizable, but, then I don't want to spent hours on customization. I decided to go with Arch Linux. When I checked out, 2011.08.19 was available and I started with that. After trying around five different times in the last three months, I gave up. Kept having problems with pacman update and glibc. I tried 2012.08.04 when it became available, but, i got problems with grub after rebooting.</p>
<p style='text-align: justify;'>Just recently, 2012.09.07 became available and I decided to try this out. Followed the instructions given here </p>
[https://wiki.archlinux.org/index.php/Beginners%27_Guide](https://wiki.archlinux.org/index.php/Beginners%27_Guide)
<p style='text-align: justify;'>to the letter. And viola, I successfully booted in Arch Linux. And it boots well even with just 256MB RAM allotted to it in VM. Took XFCE for a spin and gonna check out KDE along with Cinnamon and Mate as well.</p>
<p style='text-align: justify;'>Arch was not so tough to install after all. One just has to follow the instructions. Next step is to see if I can contribute to Arch Linux in any way.</p>

View File

@ -0,0 +1,25 @@
---
author: Sanchayan Maity
title: Audio Generation With PWM
tags: audio, PWM
---
<p style='text-align: justify;'>One of the few ways of generating audio in an embedded system is the use of PWM. So, lets get straight to the point on to how to achieve this.</p>
<p style='text-align: justify;'>Use the software from the below link to generate an array, whose values will be used to change the duty cycle of the wave.</p>
[http://www.cjseymour.plus.com/wavtocode/wavtocode.htm](http://www.cjseymour.plus.com/wavtocode/wavtocode.htm)
<p style='text-align: justify;'>Use the array generated from above in your code.</p>
<p style='text-align: justify;'>A few assumptions first, before we begin. The .wav file you have  uses 8Ksps and has 8 bits per sample.</p>
<p style='text-align: justify;'>For adequate filtering purposes, the PWM frequency has to be preferably ten times the highest voice frequency present in the sample. Although the audio frequency range is from 20 Hz to 20 KHz, the range of 20 Hz to 3400 Hz is enough for most purposes. So, let's set the PWM frequency at 40 KHz. Now, the playback rate has to match the sampling frequency rate. This implies that, before changing the duty cycle of the wave being generated, using the next value in the array, three samples have to be inserted in between the current sample and the next sample. These samples can be kept the same as the present sample or can be generated using interpolation. Using interpolation improves the sound quality and reduces distortion.  For the next part, let's assume you have managed to successfully generate the PWM wave.</p>
<p style='text-align: justify;'>Next comes the filter. The filter becomes easy to design, due to the wide seperation between the PWM frequency and the frequency range of interest. One can design a filter using software tools like FilterPro provided by Texas Instruments. The higher the order of the filter, the better the audio output generated by the filter. For the filter, one can keep a passband frequency of 4 KHz and a stopband frequency of 5.6 KHz. Keeping the allowed ripple in the passband at 0.5 dB and a stopband attenuation at -45 dB would give a four stage, eigth order filter. Keeping an allowed ripple of 0.5 dB, allows us to use the Chebysev response, for achieving a sharper cut off. Using a Butterworth response, would not give a sharp enough cut off. If you are using FilterPro, try and see what happens if you change those parameters. For example, what happens, if i make the stopband frequency have a value of 5 KHz?</p>
<p style='text-align: justify;'>The filter seperates all the high frequency components and leaves us with a waveform which is a rough enough replica of the original signal of interest. You can record the output of the filter by connecting it to the microphone input of your PC and recording it with a player like Goldwave, using which you can also do some pretty cool analysis.</p>
<p style='text-align: justify;'>You might be wondering about the assumption i told you about, just in case you still didn't get it. The number of samples to be inserted in between will depend on the sampling rate. In my case, i used a PWM frequency of 40 KHz, so i inserted four samples in between. Refer the below figure. Depending upon the code you write and how you have to set up PWM for your processor, you might have to scale the values of the array and the bits per sample will be important for this scaling.</p>
[![](http://coherentmusings.files.wordpress.com/2012/10/playback2.jpg)](http://coherentmusings.files.wordpress.com/2012/10/playback2.jpg)

View File

@ -0,0 +1,207 @@
---
author: Sanchayan Maity
title: Beagleboard-xM GPIO Kernel Module Driver
tags: beagleboard, gpio driver
---
<p style='text-align: justify;'>Whenever i search for a particular implementation, of let's say GPIO, only user space implementation comes up in search results which use the sysfs interface. Being more interested in hardware and the kernel side of things, i thought i did start practicing writing some kernel module drivers. So, here is my first simple GPIO kernel module driver for the Beagleboard-xM.</p>
<p style='text-align: justify;'>I have set up my environment using Buildroot from the below link. I used this, because at some point i intend to use the camera module and Max Galemin has integrated those packages in his buildroot version. OpenCV 2.3.1 is supported in this buildroot, so that's a very good plus point. Set up your environment using the instructions given on the below link or i assume you can use the standard buildroot available as well.</p>
[http://blog.galemin.com/2012/03/buildroot-2012-02-for-beagleboard-xm-with-li-5m03-mt9p031-camera-support/](http://blog.galemin.com/2012/03/buildroot-2012-02-for-beagleboard-xm-with-li-5m03-mt9p031-camera-support/)
<p style='text-align: justify;'>You can do the default build or you might want to add support for php, python, lighthttpd, nfs, thttpd and a few other things as well.</p>
<p style='text-align: justify;'>Below I am posting the source code for the driver module, the user space application and the Makefile.</p>
The Driver Module:  gpio.ko
<pre><code>
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/init.h"
#include "linux/platform_device.h"
#include "linux/gpio.h"
#include "linux/fs.h"
#include "linux/errno.h"
#include "asm/uaccess.h"
#define GPIO_NUMBER    149 //User LED 0. GPIO number 149. Page 71 of BB-xM Sys Ref Manual.
static int init_result;
static ssize_t gpio_read( struct file* F, char *buf, size_t count, loff_t *f_pos )
{
char buffer[10];
int temp = gpio_get_value(GPIO_NUMBER);
sprintf( buffer, "%1d" , temp );
count = sizeof( buffer );
if( copy_to_user( buf, buffer, count ) )
{
return -EFAULT;
}
if( *f_pos == 0 )
{
*f_pos += 1;
return 1;
}
else
{
return 0;
}
}
static ssize_t gpio_write( struct file* F, const char *buf, size_t count, loff_t *f_pos )
{
printk(KERN_INFO "Executing WRITE.\n");
switch( buf[0] )
{
case '0':
gpio_set_value(GPIO_NUMBER, 0);
break;
case '1':
gpio_set_value(GPIO_NUMBER, 1);
break;
default:
printk("Wrong option.\n");
break;
}
return count;
}
static int gpio_open( struct inode *inode, struct file *file )
{
return 0;
}
static int gpio_close( struct inode *inode, struct file *file )
{
return 0;
}
static struct file_operations FileOps =
{
.open         = gpio_open,
.read         = gpio_read,
.write         = gpio_write,
.release     = gpio_close,
};
static int init_gpio(void)
{
init_result = register_chrdev( 0, "gpio", &FileOps );
if( 0 > init_result )
{
printk(KERN_ALERT "Device Registration failed\n");
return -1;
}
else
{
printk(KERN_ALERT "Major number is: %d\n",init_result);
return 0;
}
}
void cleanup_gpio(void)
{
unregister_chrdev( init_result, "gpio" );
printk(KERN_ALERT "Device unregistered\n");
}
module_init(init_gpio);
module_exit(cleanup_gpio);
MODULE_AUTHOR("Sanchayan");
MODULE_LICENSE("GPL");
</code></pre>
The User Space Application: gpio_app.c
<pre><code>
#include<stdio.h>
#include <fcntl.h>
int main(void)
{
int fd;
char gpio_buffer[10];
char choice[10];
fd = open( "/dev/gpio", O_RDWR );
printf( "Value of fd is: %d", fd );
if( fd < 0 )
{
printf("Cannot open device \t");
printf(" fd = %d \n",fd);
return 0;
}
printf("\nPlease enter choice: \t");
scanf( "%s", choice );
printf("Your choice is: %s \n", choice );
write( fd, choice, 1 );
read( fd, gpio_buffer, 1);
printf("GPIO value is: %s \n", gpio_buffer );
if( 0 != close(fd) )
{
printf("Could not close device\n");
}
return 0;
}
</code></pre>
Makefile
<pre><code>
KERN_SRC=/home/vm/buildroot/output/build/linux-3.2.8
KERN_COMPILER=/opt/CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/bin
obj-m := gpio.o
all:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` modules
clean:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` clean
</code></pre>
<p style='text-align: justify;'>The path for your kernel source and path for the CodeSourcery toolchain can and mostly will be different. So change them, as per your environment.</p>
<p style='text-align: justify;'>After this, compile your kernel driver module by typing "sudo make" at the command line prompt. After this, cross compile your user space application by typing</p>
`arm-none-linux-gnueabi-gcc   gpio_app.c   -o   gpio_app`
<p style='text-align: justify;'>I have assumed that the cross compiler path has been added to your path environment variable.</p>
<p style='text-align: justify;'>You will now have a gpio.ko file and a gpio_app executable. Transfer these files to your beagleboard. I use the "scp" command to transfer files to my board over the ssh connection.</p>
<p style='text-align: justify;'>On the command prompt of your beagleboard, do "insmod gpio.ko". Find out the major number by checking the dmesg output or /proc/devices.</p>
<p style='text-align: justify;'>Assuming for example, that the major number is 248, do "mknod /dev/gpio c 248 0".</p>
<p style='text-align: justify;'>The mknod command has to be run for creation of the device node. If mknod is not run, you can see your module loaded using lsmod, but, you won't notice any entry in the /dev directory with which to access it through your user space application. After running mknod you can see the gpio driver entry in the /dev directory with ls /dev.</p>
<p style='text-align: justify;'>By default, the user led's 0 and 1 which are connected on GPIO 149 and 150, are used for indicating mmc card access activity and heartbeat.</p>
<p style='text-align: justify;'>You can turn these dafault behaviour off by first entering the /sys/class/gpio/beagleboard::usr0 and /sys/class/gpio/beagleboard::usr1 directory. After this, do "cat trigger". You should see the default behaviour marked.</p>
<p style='text-align: justify;'>Now, change the default behaviour for both by doing "echo none > trigger" in their respective directories.</p>
<p style='text-align: justify;'>Now, run the user space application by typing ./gpio_app on the command line prompt. You will be prompted to enter the value "0" or "1" to turn off or turn on the led. You can observe the state of the led pin on your board after this.</p>
<p style='text-align: justify;'>Refer the Linux Device Drivers book by Corbet, Rubini and Greg Kroah Hartman. The second and third chapters are important for this tutorial.</p>

View File

@ -0,0 +1,260 @@
---
author: Sanchayan Maity
title: Device node creation without using "mknod"
tags: beagleboard, gpio driver
---
<p style='text-align: justify;'>In my last post, where i showed how to write a character gpio driver, i had used mknod for device node creation. Without mknod the device files would not have been created under /dev. So, we had to manually create the device node under /dev using mknod. Now, cannot this manual work be done away with? Of course, it can be done!!.</p>
<p style='text-align: justify;'>The automatic creation of device files can be handled with udev. One has to ensure that the major and minor numbers assigned to a device controlled by the driver are exported to user space through the sysfs interface. To know more about this, read "The Linux Device Module" chapter from the Linux Device Drivers book.</p>
<p style='text-align: justify;'>Below i am posting the source code for the driver module, the user space application and the Makefile. The user space application and Makefile remain the same. I have only changed the name of the device node under /dev from gpio to gpio_drv. So, the user space application code accordingly reflects this.</p>
**The Driver Module:**
<pre><code>
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/init.h"
#include "linux/platform_device.h"
#include "linux/gpio.h"
#include "linux/fs.h"
#include "linux/errno.h"
#include "asm/uaccess.h"
#include "linux/version.h"
#include "linux/types.h"
#include "linux/kdev_t.h"
#include "linux/device.h"
#include "linux/cdev.h"
#define GPIO_NUMBER    149     //User LED 0. GPIO number 149. Page 71 of BB-xM Sys Ref Manual.
static dev_t first;         // Global variable for the first device number
static struct cdev c_dev;     // Global variable for the character device structure
static struct class *cl;     // Global variable for the device class
static int init_result;
static ssize_t gpio_read( struct file* F, char *buf, size_t count, loff_t *f_pos )
{
char buffer[10];
int temp = gpio_get_value(GPIO_NUMBER);
sprintf( buffer, "%1d" , temp );
count = sizeof( buffer );
if( copy_to_user( buf, buffer, count ) )
{
return -EFAULT;
}
if( *f_pos == 0 )
{
*f_pos += 1;
return 1;
}
else
{
return 0;
}
}
static ssize_t gpio_write( struct file* F, const char *buf, size_t count, loff_t *f_pos )
{
printk(KERN_INFO "Executing WRITE.\n");
switch( buf[0] )
{
case '0':
gpio_set_value(GPIO_NUMBER, 0);
break;
case '1':
gpio_set_value(GPIO_NUMBER, 1);
break;
default:
printk("Wrong option.\n");
break;
}
return count;
}
static int gpio_open( struct inode *inode, struct file *file )
{
return 0;
}
static int gpio_close( struct inode *inode, struct file *file )
{
return 0;
}
static struct file_operations FileOps =
{
.owner        = THIS_MODULE,
.open         = gpio_open,
.read         = gpio_read,
.write        = gpio_write,
.release      = gpio_close,
};
static int init_gpio(void)
{
//init_result = register_chrdev( 0, "gpio", &FileOps );
init_result = alloc_chrdev_region( &first, 0, 1, "gpio_drv" );
if( 0 > init_result )
{
printk( KERN_ALERT "Device Registration failed\n" );
return -1;
}
//else
//{
//    printk( KERN_ALERT "Major number is: %d\n",init_result );
//    return 0;
//}
if ( (cl = class_create( THIS_MODULE, "chardev" ) ) == NULL )
{
printk( KERN_ALERT "Class creation failed\n" );
unregister_chrdev_region( first, 1 );
return -1;
}
if( device_create( cl, NULL, first, NULL, "gpio_drv" ) == NULL )
{
printk( KERN_ALERT "Device creation failed\n" );
class_destroy(cl);
unregister_chrdev_region( first, 1 );
return -1;
}
cdev_init( &c_dev, &FileOps );
if( cdev_add( &c_dev, first, 1 ) == -1)
{
printk( KERN_ALERT "Device addition failed\n" );
device_destroy( cl, first );
class_destroy( cl );
unregister_chrdev_region( first, 1 );
return -1;
}
return 0;
}
void cleanup_gpio(void)
{
//unregister_chrdev( init_result, "gpio" );
cdev_del( &c_dev );
device_destroy( cl, first );
class_destroy( cl );
unregister_chrdev_region( first, 1 );
printk(KERN_ALERT "Device unregistered\n");
}
module_init(init_gpio);
module_exit(cleanup_gpio);
MODULE_AUTHOR("Sanchayan");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Beagleboard-xM GPIO Driver");
</code></pre>
**The User Space Application:**
<pre><code>
#include "stdio.h"
#include "fcntl.h"
int main(void)
{
int fd;
char gpio_buffer[10];
char choice[10];
fd = open( "/dev/gpio_drv", O_RDWR );
printf( "Value of fd is: %d", fd );
if( fd < 0 )
{
printf("Cannot open device \t");
printf(" fd = %d \n",fd);
return 0;
}
printf("\nPlease enter choice: \t");
scanf( "%s", choice );
printf("Your choice is: %s \n", choice );
write( fd, choice, 1 );
read( fd, gpio_buffer, 1);
printf("GPIO value is: %s \n", gpio_buffer );
if( 0 != close(fd) )
{
printf("Could not close device\n");
}
return 0;
}
</code></pre>
**Makefile:**
<pre><code>
# Cross compilation Makefile for ARM
KERN_SRC=/home/vm/buildroot/output/build/linux-3.2.8
KERN_COMPILER=/opt/CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/bin
obj-m := gpio.o
all:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` modules
clean:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` clean
</code></pre>
<p style='text-align: justify;'>The path for your kernel source and path for the CodeSourcery toolchain can and mostly will be different. So change them, as per your environment.</p>
<p style='text-align: justify;'>After this, compile your kernel driver module by typing “sudo make” at the command line prompt. After this, cross compile your user space application by typing</p>
`arm-none-linux-gnueabi-gcc   gpio_app.c   -o   gpio_app`
<p style='text-align: justify;'>I have assumed that the cross compiler path has been added to your path environment variable.</p>
<p style='text-align: justify;'>You will now have a gpio.ko file and a gpio_app executable. Transfer these files to your beagleboard. I use the “scp” command to transfer files to my board over the ssh connection.</p>
<p style='text-align: justify;'>On the command prompt of your beagleboard, do “insmod gpio.ko”.</p>
<p style='text-align: justify;'>**This time, you won't have to do the mknod for device node creation under /dev. You can cd to /sys/class and you will find the chardev entry there. Do ls /dev and you will find gpio_drv already present without having run mknod.**</p>
<p style='text-align: justify;'>By default, the user leds 0 and 1 which are connected on GPIO 149 and 150, are used for indicating mmc card access activity and heartbeat.</p>
<p style='text-align: justify;'>You can turn these dafault behaviour off by first entering the /sys/class/gpio/beagleboard::usr0 and /sys/class/gpio/beagleboard::usr1 directory. After this, do “cat trigger”. You should see the default behaviour marked.</p>
<p style='text-align: justify;'>Now, change the default behaviour for both by doing “echo none > trigger” in their respective directories.</p>
<p style='text-align: justify;'>Now, run the user space application by typing ./gpio_app on the command line prompt. You will be prompted to enter the value “0″ or “1″ to turn off or turn on the led. You can observe the state of the led pin on your board after this.</p>
<p style='text-align: justify;'>Refer the Linux Device Drivers book by Corbet, Rubini and Greg Kroah Hartman. The second, third and fourteenth chapters are important for this tutorial.</p>
<p style='text-align: justify;'>Refer this link</p>
[http://www.linuxforu.com/2011/04/character-device-files-creation-operations/](http://www.linuxforu.com/2011/04/character-device-files-creation-operations/http://).
<p style='text-align: justify;'>as well. Thanks to Anil Pugalia for his wonderful series on Linux Device Drivers available on</p>
[http://www.linuxforu.com/tag/linux-device-drivers-series/](http://www.linuxforu.com/tag/linux-device-drivers-series/).

View File

@ -0,0 +1,59 @@
---
author: Sanchayan Maity
title: PCB Design Fundamentals - Impedance Matching
tags: pcb design, impedance matching, hardware
---
<p style='text-align: justify;'>Once upon a time, I use to think that the printed circuit boards were nothing, but, just a means for connecting the various components to each other and the system design was much more important. Working in the industry for the past two and half years, has made me realize that even though the system design is quiet important, a well designed system is not going to work without proper attention being paid to the PCB Design.</p>
<p style='text-align: justify;'>There are various aspects to PCB design like crosstalk, EMI and EMC, differential signalling and one of them is impedance matching. All this is known in the industry under the broad term and umbrella name of "Signal Integrity". An impedance mismatch can affect the signal integrity to a great extent and the damage done depends on the impedance mismatch and the complexity of the interconnect network in which the impedance mismatch is occurring.</p>
<p style='text-align: justify;'>The concept of impedance mismatch will be known to anyone who has studied Transmission Lines in Electromagnetics or Microwave Engineering. Now, you might be wondering how does transmission lines come into the picture??!!.</p>
<p style='text-align: justify;'>At high frequencies, the interconnect lines do not function as simple interconnects, but, start acting as transmission lines at high frequencies. Also, if you remember control systems, at low frequencies a lumped parameter model is applicable, while at high frequencies, distributed parameter model becomes applicable. If i were to put it more simply, when the wavelength of the signal is less than the length of the interconnect on which it is propagating, you can treat them as simple interconnects and apply the lumped parameter model, meaning that you can model the behavior of the interconnect using a simple RC model. The response of a RC model is well-known to all in the field of electronics. At high frequencies, where the wavelength of the signal is less than the length of the interconnect on which it is propagating, you can no longer consider them as mere interconnects, but, the distributed parameter model becomes applicable and the transmission line concepts need to be applied now. A simple RC model will no longer suffice for modelling the behavior of interconnects.</p>
<p style='text-align: justify;'>In actual production environments and industry, PCB design and signal integrity issues like impedance mismatch are done and checked using software like PADS and Allegro. For a demonstration, I will show you how an impedance mismatch degrades signal integrity by using an example and lattice diagrams.</p>
<p style='text-align: justify;'>An impedance mismatch occurs, when the characteristic impedance of the transmission line doesn't match the load or another transmission line which it is driving. If you remember the formula for reflection co-efficient τ = (Zl - Zo)/(Zl + Zo). So, if the load impedance is not equivalent to the characteristic impedance, the reflection co-efficient will have a value, ideally which should have been zero. This impedance mismatch results in a part of the signal being transmitted and part of it being reflected. The reflected signal then propagates back towards the source, where depending on the impedance of the source and the line, another reflection might or might not occur. This goes on till this reaches steady state.</p>
<p style='text-align: justify;'>Now, let's analyze this using a lattice diagram. A lattice diagram looks as shown below.</p>
[![lattice](http://coherentmusings.files.wordpress.com/2013/01/lattice.jpg)](http://coherentmusings.files.wordpress.com/2013/01/lattice.jpg)
<p style='text-align: justify;'>A lattice diagram  is a graphical technique used to solve the multiple reflections on a transmission line with linear loads.  The left- and right-hand vertical lines represent the source end (z = 0) and load end (z = l) of the transmission line. The diagonal lines contained between the vertical lines represent the signal bouncing back and forth between the source and the load. The diagram progressing from top to bottom represents increasing time. Notice that the time increment is equal to the time delay τd of the transmission line, and the reflection coefficients looking into the source and into the load are labeled at the top of the vertical bars. The lowercase letters represent the magnitude of the reflected signal traveling on the line. The uppercase letters represent the voltages seen at the source, and the primed uppercase letters represent the voltage seen at the load end of the line. The delay for the signal to propagate from one end of the transmission line to the other is given by " l √LC ", where l is the length of the transmission line and L and C are the inductance and capacitance values per unit length.</p>
<p style='text-align: justify;'>Consider the case of reflections when the impedance of the source Rs is less than the load impedance Zo.</p>
[![Example](http://coherentmusings.files.wordpress.com/2013/01/example.jpg)](http://coherentmusings.files.wordpress.com/2013/01/example.jpg)
<p style='text-align: justify;'>Rs for the above example is 25 ohms and the load impedance is ∞. This gives a reflection co-efficient of 1 at the load end and (-1/3) at the source end by using the generalized formula of (Zo2 - Zo1)/(Z02 + Z01).</p>
[![Laex](http://coherentmusings.files.wordpress.com/2013/01/laex.jpg)](http://coherentmusings.files.wordpress.com/2013/01/laex.jpg)
<p style='text-align: justify;'>The lattice diagram will come out as like shown below. The calculations for the lattice diagram will be as below.</p>
<p style='text-align: justify;'>1. A 2V signal is going to be driven on the line. Using a simple voltage divider, the voltage driven at point A will be 2 x 50/(25 + 50) = 1.33V. When 1.33V reaches the load end, this will be multiplied by the reflection co-efficient at the load end, viz. 1. So, the entire signal will be reflected back onto the line, and the voltage will be 1.33V as shown, propagating towards the source. At the load end, 1.33V + 1.33V = 2.66V will be the voltage.</p>
<p style='text-align: justify;'>2. The reflection co-efficient at the source end is (25 - 50)/(25 + 50) = (-1/3). The incoming 1.33V signal toward the source will be multiplied by (-1/3) and then propagate back towards the load. So, (-1/3) x 1.33 = -0.443V signal will now propagate towards the load end. At the source, the voltage is given by 1.33V + 1.33V + (-0.44V) = 2.22V.</p>
<p style='text-align: justify;'>3. Again at the load end, the -0.443V signal will be reflected back fully due to the reflection co-efficient being 1 and you can see a -0.443V signal propagating back toward the source in the above lattice diagram. At the load end, the voltage will be 2.66-0.443-0.443 = 1.77V.</p>
<p style='text-align: justify;'>4. On reaching the source end, the (-0.443V) signal will be multiplied by a reflection co-efficient of (-1/3) giving a 0.148V signal again propagating towards the load as shown in the lattice diagram. The voltage at the source end will be 2.22-0.443+0.148 = 1.92V.</p>
<p style='text-align: justify;'>5. On reaching the load end, the 0.148V signal will be multiplied by the load reflection co-efficient of 1 and this will travel back towards the source. At the load end, the voltage will be 1.77+0.148+0.148 = 2.07V.</p>
<p style='text-align: justify;'>Carrying out the calculations similarly for the falling edge and using the above calculations for rising edge, will result in a waveform as shown below.</p>
[![Time](http://coherentmusings.files.wordpress.com/2013/01/time.jpg)](http://coherentmusings.files.wordpress.com/2013/01/time.jpg)
<p style='text-align: justify;'>As you can see, a 2V signal driven on the transmission line resulted in a signal much different than what it should have been, due to the impedance mismatch.</p>
<p style='text-align: justify;'>Also, notice the particular ringing effect in the waveform. This happens when the impedance of the source Rs is less than the load impedance Zo, so called an over driven transmission line. The opposite case of an under drive transmission line, with Rs > Zo, will not show ringing, but will result in such a distortion.</p>
<p style='text-align: justify;'>The above is just a very simple case. An analysis in the manner of above for multiple cascaded transmission lines will be much involved and such a case can severely degrade the signal integrity.</p>
<p style='text-align: justify;'>The impedance matching is taken care of by controlling the length of the transmission line, it's width and height of the line from the reference ground plane while designing the PCB using EDA software tools.</p>
<p style='text-align: justify;'>**N.B.** The example is an unsolved example taken from the book "Advanced Signal Integrity for High Speed Digital Designs" by Stephen Hall and Howard Heck. The calculations are done by me and the figures have been copied from the book. In case any publisher or author of the book has any copyright issue with this, drop me a mail on victorascroft@gmail.com. I will take them down and replace them with different photographs and example.</p>

View File

@ -0,0 +1,109 @@
---
author: Sanchayan Maity
title: Starting with GTK+ Programming
tags: gtk, linux
---
<p style='text-align: justify;'>It's been a while since my last post in January. I shifted to Bangalore for work and took some time to get used to the new work environment and schedule. Anyways, let's get on with the topic at hand.</p>
<p style='text-align: justify;'>My current work involves a lot of GUI development on WinCE in C# and since i wanted to work on Linux, it left much to be desired. So, i thought why not do some GUI development on Linux. I could have opted for Qt and an Integrated Development Environment does speed up the work a lot and though developing GUI applications is a breeze in C#/QT with an IDE, i don't like GUI development and it becomes boring if things become too simple, like it happened with C#. So, i chose GTK+ for this task and Nano as the editor of my choice on my new Arch Linux installation to amuse myself and keep myself from getting bored.</p>
<p style='text-align: justify;'>Also, on a different note, i recently tried my hands on Gentoo. For any package installation, having to download the source, compile the package and then install, turned me off it. Actually, i have wanted to contribute to the open source community for a while, and this [http://blog.dastergon.gr/get-involved-in-gentoo-linux/](http://blog.dastergon.gr/get-involved-in-gentoo-linux/) motivated me to go ahead with Gentoo, but, i was put off by the package management. I will stick with Arch.</p>
I am using Xfce as my desktop environment in Arch Linux. At first, i was worried about having to take care of package installation and dependencies for starting with GTK programming, but, i think during the Xfce installation or something else this was taken care of.
First i just typed the below program in the Nano editor.
{% highlight c %}
#include <gtk/gtk.h>
static void hello( GtkWidget *widget, gpointer data )
{
g_print ("Hello World\n");
}
static gboolean delete_event( GtkWidget *widget, GdkEvent *event, gpointer data )
{
g_print ("Delete event occurred\n");
return FALSE;
}
static void destroy( GtkWidget *widget, gpointer data )
{
gtk_main_quit ();
}
int main( int argc, char *argv[] )
{
GtkWidget *window;
GtkWidget *button;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete-event", G_CALLBACK (delete_event), NULL);
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
button = gtk_button_new_with_label ("Hello World");
g_signal_connect (button, "clicked", G_CALLBACK (hello), NULL);
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
gtk_container_add (GTK_CONTAINER (window), button);
gtk_widget_show (button);
gtk_widget_show (window);
gtk_main ();
return 0;
}
{% endhighlight %}
 You can compile the above code by typing the following on command line.
{% highlight c %}
gcc -o hello hello.c `pkg-config --libs --cflags gtk+-2.0`
{% endhighlight %}
After compilation, run the output file generated and see the output. I did not intend much with this tutorial, but, just wanted to share. This post is more sort of an incoherent musing and working on GUI's is boring and i can't make head or tails of how to make good user interfaces.
You can find a detail tutorial and explanation of various functions, API's and callbacks on the GNOME development center below.
[https://developer.gnome.org/gtk-tutorial/2.24/book1.html](https://developer.gnome.org/gtk-tutorial/2.24/book1.html).
I hope to put up some articles soon on PCB design, which i have been putting off and some other stuff related to programming may be.

View File

@ -0,0 +1,18 @@
---
author: Sanchayan Maity
title: PCB Design Fundamentals - A Design Guide
tags: pcb design, hardware
---
<p style='text-align: justify;'>Recently I had the privilege to get some hands on training from one of the senior guys working at the Switzerland headquarters of our company. Till date, I had only learned about PCB design by reading books with a bit of experience coming from having dealt with PCB design vendors. It was very interesting to see the actual application of the principles i learned from someone very knowledgeable and experienced, first hand. The most interesting part of the training was the part where the concept of return paths was explained and how to take care of this while doing PCB design. It is something which i had not understood previously during my reading of books and application notes.</p>
<p style='text-align: justify;'>Rather than regurgitating what I learned and doing a shoddy job of it, I will just share the link to the design guide which my experienced colleagues have put out there for the benefit of customers of our company and others who might benefit from our products and work.</p>
[http://developer.toradex.com/hardware-resources/arm-family/carrier-board-design ](http://developer.toradex.com/hardware-resources/arm-family/carrier-board-design)
<p style='text-align: justify;'>On the above link you will find a link to the Apalis Carrier Board Design Guide which you should read.</p>
Enjoy!!!

View File

@ -0,0 +1,110 @@
---
author: Sanchayan Maity
title: P/Invoke and Marshaling on the Microsoft .NET Compact Framework
tags: wince, .net cf
---
<p style='text-align: justify;'>Even though a lot of functionality is provided while working at higher levels for application development, situations arise where you have to use a third party library in your code or may be a library which you yourself developed. Often this library might be in some other language and you will only have access to the Dynamic Link Library or Static Library.</p>
<p style='text-align: justify;'>I faced such a situation recently where i had a library which was written in C and the DLL was available for it which i had to use in my C# application. Now had i been working on the desktop it would have been much easier since the full .NET Compact Framework provides much more features than the .NET Compact Framework 3.5 which i had to use, since i was working on Windows CE.</p>
<p style='text-align: justify;'>The Platform Invoke is a feature of C# which allows you to call functions which are only available through a native DLL. You can get an overview of this feature at the below link. This is a general explanation of Platform Invoke feature.</p>
[http://msdn.microsoft.com/en-us/magazine/cc164123.aspx](http://msdn.microsoft.com/en-us/magazine/cc164123.aspx)
<p style='text-align: justify;'>What helped me were the below two links which discuss Platform Invoke with respect to the .NET Compact Framework.</p>
[http://www.codeproject.com/Articles/5888/An-Introduction-to-P-Invoke-and-Marshaling-on-the](http://www.codeproject.com/Articles/5888/An-Introduction-to-P-Invoke-and-Marshaling-on-the)
[http://www.codeproject.com/Articles/5890/Advanced-P-Invoke-on-the-Microsoft-NET-Compact-Fra](http://www.codeproject.com/Articles/5890/Advanced-P-Invoke-on-the-Microsoft-NET-Compact-Fra)
<p style='text-align: justify;'>I will not give a detail explanation of my use case, since that should be clear after one has read the above two articles.</p>
C :
<pre><code>
typedef struct
{
char *snsAccessID;
char *snsSecretKey;
char *snsPath;
char *snsTopicName;
char *snsTopicAmazonResourceName;
char *snsDisplayName;
char *snsOwnerId;
} snsTopic, *HSNS;
BOOL SnsOpenTopic(char *accessID, char *secretKey, char *ownerId, char *path, char *topicName, char *displayName, HSNS *snsTopicHandle);
</code></pre>
C# :
<pre><code>
[StructLayout(LayoutKind.Sequential)]
public unsafe struct HSNS
{
public char *snsAccessID;
public char *snsSecretKey;
public char *snsPath;
public char *snsTopicName;
public char *snsTopicAmazonResourceName;
public char *snsDisplayName;
public char *snsOwnerId;
}
[DllImport("Cloud.dll", SetLastError = true)]
public unsafe static extern Boolean SnsOpenTopic(Byte* accessID, Byte* secretKey, Byte* ownerId, Byte* path, Byte* topicName, Byte* displayName, ref HSNS snsAcsTopic);
public Byte[] topicName = Encoding.ASCII.GetBytes("CSharpACSAlert\0");
HSNS snsAcsTopic = new HSNS();
fixed (Byte* accessID = &AWS_ACCOUNT_ACCESS_ID[0])
{
fixed (Byte* secretKey = &AWS_ACCOUNT_SECRET_KEY[0])
{
fixed (Byte* ownerId = &AWS_ACCOUNT_OWNER_ID[0])
{
fixed (Byte* path = &AWS_SNS_SINGAPORE_REGION[0])
{
fixed (Byte* ptrTopicName = &topicName[0])
{
fixed (Byte* ptrDisplayName = &displayName[0])
{
fixed (Byte* ptrEmailId = &emailId[0])
{
fixed (Byte* ptrSubject = &subject[0])
{
fixed (Byte* ptrMessage = &message[0])
{
if (SnsOpenTopic(accessID, secretKey, ownerId, path, ptrTopicName, ptrDisplayName, ref snsAcsTopic))
{
if (SnsCreateTopic(snsAcsTopic))
{
MessageBox.Show("Done");
}
else
{
MessageBox.Show("Could not create topic");
MessageBox.Show(Convert.ToString(Marshal.GetLastWin32Error()));
}
}
else
{
MessageBox.Show("Could not open topic");
MessageBox.Show(Convert.ToString(Marshal.GetLastWin32Error()));
}
}
}
}
}
}
}
}
}
}
</code></pre>
<p style='text-align: justify;'>Above is only a snippet of my code and not the complete source. But, this is complete enough to demonstrate how to use Platform Invoke for such a case. First I have given the template which is being used in C and then i have posted the C# equivalent to be used with Platform Invoke.</p>
<p style='text-align: justify;'>One of the important points to note here is that if your function expects a pointer to an ANSI string you will have to convert to ASCII encoding by using "Encoding.ASCII.GetBytes". Then you need to get the pointer to it which has to be passed to the argument expecting an ANSI string. This has to be done since everything in .NET CF3.5 is handled in Unicode. I had a hunch about this being the reason of why my code was not working but I was not sure till I came across those two articles. If your character pointers are "Wide" or Unicode style viz. WChar_t* to be more specific you can skip the encoding conversion.</p>

View File

@ -0,0 +1,168 @@
---
author: Sanchayan Maity
title: Sending HTTP Request on Linux
tags: http
---
You can use this code to send HTTP request on Linux.
<pre><code>
#include "stdio.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "stdlib.h"
#include "netdb.h"
#include "string.h"
int create_tcp_socket();
char *get_ip(char *host);
char *build_get_query(char *host, char *page);
void usage();
#define HOST "coding.debuntu.org"
#define PAGE "/"
#define PORT 80
#define USERAGENT "HTMLPOST 1.0"
int main(int argc, char **argv)
{
struct sockaddr_in *remote;
int sock;
int tmpres;
char *ip;
char *get;
char buf[4096];
char *host;
char *page;
if(argc == 1){
usage();
exit(2);
}
host = argv[1];
if(argc > 2){
page = argv[2];
}else{
page = PAGE;
}
sock = create_tcp_socket();
ip = get_ip(host);
fprintf(stderr, "IP is %s\n", ip);
remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
remote->sin_family = AF_INET;
tmpres = inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr)));
if( tmpres < 0)
{
perror("Can't set remote->sin_addr.s_addr");
exit(1);
}else if(tmpres == 0)
{
fprintf(stderr, "%s is not a valid IP address\n", ip);
exit(1);
}
remote->sin_port = htons(PORT);
if(connect(sock, (struct sockaddr *)remote, sizeof(struct sockaddr)) < 0){
perror("Could not connect");
exit(1);
}
get = build_get_query(host, page);
fprintf(stderr, "Query is:\n<<START>>\n%s<<END>>\n", get);
//Send the query to the server
int sent = 0;
while(sent < strlen(get))
{
tmpres = send(sock, get+sent, strlen(get)-sent, 0);
if(tmpres == -1){
perror("Can't send query");
exit(1);
}
sent += tmpres;
}
//now it is time to receive the page
memset(buf, 0, sizeof(buf));
int htmlstart = 0;
char * htmlcontent;
while((tmpres = recv(sock, buf, 4096, 0)) > 0){
if(htmlstart == 0)
{
/* Under certain conditions this will not work.
* If the \r\n\r\n part is splitted into two messages
* it will fail to detect the beginning of HTML content
*/
htmlcontent = strstr(buf, "\r\n\r\n");
if(htmlcontent != NULL){
htmlstart = 1;
htmlcontent += 4;
}
}else{
htmlcontent = buf;
}
if(htmlstart){
fprintf(stdout, htmlcontent);
}
memset(buf, 0, tmpres);
}
if(tmpres < 0)
{
perror("Error receiving data");
}
free(get);
free(remote);
free(ip);
close(sock);
return 0;
}
void usage()
{
fprintf(stderr, "USAGE: htmlget host [page]\n\
\thost: the website hostname. ex: coding.debuntu.org\n\
\tpage: the page to retrieve. ex: index.html, default: /\n");
}
int create_tcp_socket()
{
int sock;
if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
perror("Can't create TCP socket");
exit(1);
}
return sock;
}
char *get_ip(char *host)
{
struct hostent *hent;
int iplen = 15; //XXX.XXX.XXX.XXX
char *ip = (char *)malloc(iplen+1);
memset(ip, 0, iplen+1);
if((hent = gethostbyname(host)) == NULL)
{
herror("Can't get IP");
exit(1);
}
if(inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, iplen) == NULL)
{
perror("Can't resolve host");
exit(1);
}
return ip;
}
char *build_get_query(char *host, char *page)
{
char *query;
char *getpage = page;
char *tpl = "POST /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n";
if(getpage[0] == '/'){
getpage = getpage + 1;
fprintf(stderr,"Removing leading \"/\", converting %s to %s\n", page, getpage);
}
// -5 is to consider the %s %s %s in tpl and the ending \0
query = (char *)malloc(strlen(host)+strlen(getpage)+strlen(USERAGENT)+strlen(tpl)-5);
sprintf(query, tpl, getpage, host, USERAGENT);
return query;
}
</code></pre>

View File

@ -0,0 +1,11 @@
---
author: Sanchayan Maity
title: A SQL Tutorial
tags: sql
---
<p style='text-align: justify;'>Not going to say much in this post. RDBMS is more or less a ubiquitous technology which everyone uses without ever realizing. With more and more embedded systems starting to run operating systems, a database is one of the most component that the OS can provide support for (though technically i guess, you could compile SQLite for your bare bone platform). Most developers at some point or another will have to get their hands dirty using SQL to access the RDBMS. So, here is a very good tutorial by Mr. Philip Greenspun which will teach you how to use SQL in general.</p>
[http://philip.greenspun.com/sql/](http://philip.greenspun.com/sql/)
Have a good time.

View File

@ -0,0 +1,13 @@
---
author: Sanchayan Maity
title: Implementing a MySQL Client
tags: mysql, wince, amazon rds
---
<p style='text-align: justify;'>Lets say a situation comes where you have to access a MySQL Server running in some part of the world and for some reason the MySQL Client/Connector is not available for your platform (i am talking of embedded, just in case you are wondering) or though the source is available, trying to get it to compile cleanly for your platform is just too much of a f****** headache. Well, a much simpler way exists. Just implement the MySQL Client/Server protocol using the article below.</p>
[http://dev.mysql.com/doc/internals/en/client-server-protocol.html](http://dev.mysql.com/doc/internals/en/client-server-protocol.html)
<p style='text-align: justify;'>If I knew such a protocol document was available which gave details on how the MySQL Client and Server communicate with each other, i could have saved so much of my time and effort. Hope if anyone else out there needs a MySQL Client/Connector to communicate with the MySQL Server and if support is not available for his platform, this will be of help.</p>
<p style='text-align: justify;'>N.B. I hope it's implicit that you need a TCP/IP connection and need to use socket programming.</p>

View File

@ -0,0 +1,102 @@
---
author: Sanchayan Maity
title: Face detection with OpenCV
tags: face detection, opencv
---
Below is the code for face detection in OpenCV.
<pre><code>
#include "opencv/cv.h"
#include "opencv/cxcore.h"
#include "opencv/highgui.h"
#include "stdio.h"
int main()
{
IplImage* frame = NULL;
const char* cascade_path = "C:\\OpenCV\\opencv\\data\\haarcascades\\haarcascade_frontalface_alt2.xml";
CvHaarClassifierCascade* hc = NULL;
CvMemStorage* storage = cvCreateMemStorage(0);
CvSize minSize = cvSize(100, 100);
CvSize maxSize = cvSize(640, 480);
CvSeq* faces = NULL;
CvCapture* input_camera = NULL;
int key = 0;
int loopCounter = 0;
hc = (CvHaarClassifierCascade*)cvLoad(cascade_path, NULL, NULL, NULL);
if (hc == NULL)
{
printf("\nLoading of classifier failed\n");
return -1;
}
input_camera = cvCaptureFromCAM(-1);
if (input_camera == NULL)
{
printf("\nCould not open camera\n");
return -1;
}
//Grabs and returns a frame from camera
frame = cvQueryFrame(input_camera);
if (frame == NULL)
{
printf("\nCould not capture frame\n");
return -1;
}
cvNamedWindow("Capturing Image ...", 0);
cvResizeWindow("Capturing Image ...",
(int) cvGetCaptureProperty(input_camera, CV_CAP_PROP_FRAME_HEIGHT),
(int) cvGetCaptureProperty(input_camera, CV_CAP_PROP_FRAME_WIDTH));
while(frame != NULL)
{
faces = cvHaarDetectObjects(frame, hc, storage, 1.2, 3, CV_HAAR_DO_CANNY_PRUNING, minSize, maxSize);
for (loopCounter = 0; loopCounter < (faces ? faces->total : 0); loopCounter++)
{
CvRect *r = (CvRect*)cvGetSeqElem(faces, loopCounter);
CvPoint pt1 = { r->x, r->y };
CvPoint pt2 = { r->x + r->width, r->y + r->height };
cvRectangle(frame, pt1, pt2, CV_RGB(0, 255, 0), 3, 4, 0);
}
//Shows a frame
cvShowImage("Capturing Image ...", frame);
// Checks if ESC is pressed and gives a delay
// so that the frame can be displayed properly
key = cvWaitKey(1);
if (key == 27)        // ESC key
{
break;
}
//Grabs and returns the next frame
frame = cvQueryFrame(input_camera);
}
//cvReleaseImage( &frame );
cvReleaseCapture(&input_camera);
cvReleaseMemStorage( &storage );
cvReleaseHaarClassifierCascade( &hc );
//Destroy the window
cvDestroyWindow("Capturing Image ...");
return 0;
}</code></pre>
<p style='text-align: justify;'>I have tested the above code on a NVidia Tegra2 with Toradex Colibri T20 module and also on my laptop. Works, but there is a bug it seems. The program crashes if I enable the cvReleaseImage() line. Not an OpenCV expert, will have to really study the functions properly before playing around I guess. Will update this as soon as i found out.</p>
<p style='text-align: justify;'>I have not explained the OpenCV installation procedure for windows as it is already documented on the OpenCV website.</p>
<p style='text-align: justify;'>For using with Beagleboard you can refer to a previous article of mine</p>
[http://coherentmusings.wordpress.com/2012/06/24/getting-started-with-opencv-on-beagleboard-xm/](http://coherentmusings.wordpress.com/2012/06/24/getting-started-with-opencv-on-beagleboard-xm/).
<p style='text-align: justify;'>You can also use OpenCV with embedded Linux if you use Buildroot or OpenEmbedded for building your image.</p>

View File

@ -0,0 +1,56 @@
---
author: Sanchayan Maity
title: Learning OpenEmbedded
tags: openembedded
---
<p style='text-align: justify;'>Recently, i was assigned a project for doing a Number Plate Recognition system on NVidia Tegra 2 based Toradex Colibri T20 module. Though the project was put on the sideline just a few days after being assigned due to another high priority task coming my way, i was able to learn how to use OpenEmbedded to some extent. In fact, most of my learning happened in my own spare time. As i was already aware that i would be working on this project, i had started playing around with OE at home and started building the image.</p>
<p style='text-align: justify;'>For building images for our modules, my company Toradex uses the oe-core build system. At first, i had no idea how to use it, how to add packages to my image and so on. I was using Arch Linux at first. Now, Arch Linux being a rolling release distribution everything stays up to date. This created it's own set of problems. Three days i just spend trying to get the initial build going as it was failing for some reason or other. For example, one of the problems i encountered was related to Perl documentation (some POD error) and later gcc4.7.2 just won't compile and kept segfaulting. On looking up, it turns out it was an Arch Linux issue and to get around this, i would have to upgrade to gcc4.7.8. At that point of time, i had no idea how to resolve this issue. It would have probably been resolved by getting the bitbake recipe for gcc4.7.8 and setting the preferred version in a bitbake recipe to 4.7.8. I didn't try this out, but, on reading the OE manual much later, i thought that it might have been the solution.</p>
<p style='text-align: justify;'>Though i was frustrated during this time, and i was also cursing Arch Linux a bit, these problems made me look at the bitbake recipes, at the source packages and the process of applying patches and trying to fix each issue which use to pop up started giving me an idea at what was going on. I started appreciating Arch Linux. But of course i couldn't do this everyday. I installed Linux Mint XFCE version and started the procedure again.</p>
<p style='text-align: justify;'>This time the process was going smoothly and when the errors did come, i was able to resolve them quickly as i knew what i had to change in the bitbake recipe to get the build going. For example a checksum error halted the build. All that had to be done, was change the checksum in the bitbake recipe to the one that was being computed.</p>
<p style='text-align: justify;'>Anyways, this was my story of playing with OE and it's now time to share the links which will help you in learning OpenEmbedded. First step, have a look at a few of the bitbake recipes and try to understand as much as you can. After that, refer the below links.</p>
1. **OpenEmbedded Manual**: [http://docs.openembedded.org/usermanual/usermanual.html](http://docs.openembedded.org/usermanual/usermanual.html)
2. **Layers FAQ**: [http://www.openembedded.org/wiki/Layers_FAQ](http://http://www.openembedded.org/wiki/Layers_FAQ)
<p style='text-align: justify;'>Read this without fail. I would have saved a lot of time had i understood the concept of layers. Wasted a day's time trying to find out how to add OpenCV package to my image. If i had understood the above, a lot of time would have been saved.</p>
3. **Adding a Package**: [http://www.openembedded.org/wiki/I_want_an_image_with_package_XYZ_installed](http://www.openembedded.org/wiki/I_want_an_image_with_package_XYZ_installed)
4. **Layer Index**: [http://layers.openembedded.org/layerindex/branch/master/layers/](http://layers.openembedded.org/layerindex/branch/master/layers/)
<p style='text-align: justify;'>The above link will be very useful for searching for already existing bitbake recipes required for adding packages</p>
5. **Yocto**: [https://www.yoctoproject.org/documentation/current](https://www.yoctoproject.org/documentation/current)
<p style='text-align: justify;'>Has some information you might not find in the OE manual or the OE manual itself links here</p>
6. **Legacy Staging**: [http://www.openembedded.org/wiki/Legacy_staging](http://www.openembedded.org/wiki/Legacy_staging)
<p style='text-align: justify;'>This will be helpful if you have to convert an OE-Classic recipe to OE-Core recipe</p>
7. **BEC Systems**: [http://bec-systems.com/site/](http://bec-systems.com/site/)
<p style='text-align: justify;'>A company website having some more useful information on OpenEmbedded</p>
8. **IRC Chat**: [http://www.openembedded.org/wiki/IRC](http://www.openembedded.org/wiki/IRC)
<p style='text-align: justify;'>Last but not the list, you can always use the IRC chat to approach people if you have some specific queries and you can't find the answers anywhere else. Do thoroughly research the answer to your query using Google and read the documentation before approaching. There will be people more than willing to help you out.</p>
9. **Writing Bitbake recipes**:
a. [http://www.multitech.net/developer/products/multiconnect-ocg/development/writing-bitbake-recipes/](http://www.openembedded.org/wiki/How_to_create_a_bitbake_recipe_for_dummies)
b. [http://www.openembedded.org/wiki/How_to_create_a_bitbake_recipe_for_dummies](http://www.openembedded.org/wiki/How_to_create_a_bitbake_recipe_for_dummies)
<p style='text-align: justify;'>Well this is the last now for sure. I have put this in the last as most probably you won't have to write a recipe. There are already recipes available for thousands of various software packages which you can use. You can search for them in the layer index link i gave earlier. You can then use those recipes. If they are not already included in the layers you have, you will figure out how to add them by following the documentation above.</p>
<p style='text-align: justify;'>Hope these incoherent musings of mine will be of use to you. The post turned out to be longer than i expected, but anyways, have a good time playing with OE and Linux. May the source be with you!</p>

View File

@ -0,0 +1,400 @@
---
author: Sanchayan Maity
title: How to write a Platform Device/Driver - ADC Driver using wm97xx codec
tags: device driver
---
<p style='text-align: justify;'>Somewhat more than two years back, while i was working in Godrej, a senior colleague from my development team gave us a lecture on how to write character drivers for Linux. Didn't understand one single thing, but since that time i have been trying to learn Linux kernel related stuff, with device drivers being the main area of focus. Of course, in between there have lot of lulls where i have hit a road block and then gone into a depression mode and then started again from where i left off, with renewed zeal and vigor.</p>
<p style='text-align: justify;'>One of the road blocks till date has been the inability to understand or picture the driver framework. Recently, after quiet a lot of effort the platform device/driver framework (may be framework isn't the correct technical term, but, anyways) became clear to me.</p>
<p style='text-align: justify;'>Let me give you a background on what i was trying to write and achieve. My company Toradex manufactures and sells embedded computer on modules. One of them is the Colibri T20 which has the NVidia Tegra 2 processor on it. There is WM9715 codec from Wolfson Microelectronics, which is an audio codec with a touch panel controller. This codec has four auxilary ADC's and the device is connected on an AC97 bus. These four ADC's can be used as general purpose ADC's, while also being used for audio and touchscreen functionality.</p>
<p style='text-align: justify;'>Now, my company provides WinCE and Linux for the modules, but, the Linux is hardly as well supported as WinCE. With the default Linux image which is provided, there is no support for being able to use all the four ADC's. Only two ADC's can be used which are made available through the power driver framework. Now what i mean by power driver framework is difficult to explain, and doing so would result in digressing from the main topic. Don't worry though, this will not affect the purpose of this tutorial. For wm9715, there is already a driver available which provides the touch screen functionality along with providing access to auxiliary ADC's. But, you need to expose the interface through some other driver or framework, for it to be usable as a general purpose ADC.</p>
<p style='text-align: justify;'>I am giving three links below and i will be using these through out the rest of the tutorial, by referring to their names instead of the URL's.</p>
1. **Header file**: [http://git.toradex.com/cgit/linux-toradex.git/tree/include/linux/wm97xx.h?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/include/linux/wm97xx.h?h=tegra)
2. **Core Driver file**: [http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/input/touchscreen/wm97xx-core.c?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/input/touchscreen/wm97xx-core.c?h=tegra)
3. **Board File**: [http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra)
<p style='text-align: justify;'>Of these, the first two files you can also find in the mainline Linux kernel.</p>
<p style='text-align: justify;'>Learn about the driver model from here, http://lxr.free-electrons.com/source/Documentation/driver-model/</p>
<p style='text-align: justify;'>**N.B.** It's assumed that you have a decent knowledge of C, Linux, Character drivers, Cross compilation and Makefile.</p>
<p style='text-align: justify;'>The first question most people will ask is, what's the use of a platform device/driver framework. For x86 based PC's, it is possible for the OS to know what all devices are present, as ACPI or USB devices make it possible for the OS to query and find out which devices are present on the system, on which it is booting. For example, a device which is attached to a PCI or USB bus, the OS can query and find out what the device does or what are it's capabilities, is it a graphics card or removable USB mass storage device.</p>
<p style='text-align: justify;'>For embedded systems, you have buses like I2c or SPI. Devices attached to these buses are not discoverable in the above sense as i tried to explain. The OS has to be explicitly told that, for example, a RTC is connected on the I2C bus at an address of 0x68. This is where the platform device/driver comes in the picture.</p>
<p style='text-align: justify;'>A board file is created for each board, which specifies the devices present on buses such as SPI and I2c. Have a look at the board file. You will find various platform devices and platform data structures which are used to register devices and relevant data with the OS. Platform data is used at later point when the OS is booting to know some specific details about the device. I will explain how the platform device and it's driver are bound to each other in a while.</p>
<p style='text-align: justify;'>Have a look at the core driver file. The first two functions to look out for are the wm97xx_probe and wm97xx_remove. These are the two common operations (probe and remove) for any platform device and driver. You can see there is a device_driver structure at the bottom of the file. Whenever a module is loaded, the first function to be called is the _init() function. In this function, the driver is registered with a call to driver_register and passing it the device_driver structure. On a call to device_register() the probe function will be called. It will do all the necessary set up required. For this example, it is allocating memory, setting up handlers, allocating devices which will be using this core driver and registering it with the input subsystem, which is used for providing the touch screen functionality. Now, this basically remains the same, but might vary slightly, for example, a true ADC driver will register itself with the Industrial IO framework (IIO).</p>
<p style='text-align: justify;'>Now, here comes the main part. For me to use the ADC's through a platform driver, i also need a platform device. So, i added a few function calls inside the probe() function for allocating platform device. This device i will be using later, how, we will come to that in a moment.</p>
<pre><code>
wm->colibriadc_dev = platform_device_alloc("colibri_adc", -1);
if (!wm->colibriadc_dev) {
ret = -ENOMEM;
goto adc_err;
}
platform_set_drvdata(wm->colibriadc_dev, wm);
wm->colibriadc_dev->dev.parent = dev;
ret = platform_device_add(wm->colibriadc_dev);
if (ret < 0)
goto adc_reg_err;
</code></pre>
<p style='text-align: justify;'>For error handling,</p>
<pre><code>
adc_reg_err:
platform_device_put(wm->colibriadc_dev);
adc_err:
platform_device_del(wm->colibriadc_dev);
</code></pre>
<p style='text-align: justify;'>Now, when the OS boots and registers and makes provision for this core driver, it will also allocate and make provision for my platform device which I created, to use this core driver. I added a platform_device pointer to the struct wm97xx which is in the header file.</p>
<pre><code>
struct platform_device *colibriadc_dev;
</code></pre>
<p style='text-align: justify;'>This pointer holds the return value of platform_device_alloc(). After the above changes, we are ready for the platform driver which we will use in conjunction with a character driver for accessing the ADC's.</p>
<pre><code>
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/init.h"
#include "linux/platform_device.h"
#include "linux/gpio.h"
#include "linux/fs.h"
#include "linux/errno.h"
#include "asm/uaccess.h"
#include "linux/wm97xx.h"
#include "linux/kdev_t.h"
#include "linux/device.h"
#include "linux/cdev.h"
#include "linux/slab.h"
#include "linux/ioctl.h"
typedef struct
{
unsigned int channelNumber;
unsigned int adcValue;
}adcData;
#define SET_ADC_CHANNEL        _IOW('q', 1, adcData *)
#define GET_ADC_DATA          _IOR('q', 2, adcData *)
static dev_t first;         // Global variable for the first device number
static struct cdev c_dev;   // Global variable for the character device structure
static struct class *cl;    // Global variable for the device class
static struct wm97xx *wm;
static int init_result;
static int adcChannel;
static int adcValue;
static ssize_t adc_read(struct file* F, char *buf, size_t count, loff_t *f_pos)
{
return -EPERM;
}
static ssize_t adc_write(struct file* F, const char *buf, size_t count, loff_t *f_pos)
{
return -EPERM;
}
static int adc_open(struct inode *inode, struct file *file)
{
return 0;
}
static int adc_close(struct inode *inode, struct file *file)
{
return 0;
}
static long adc_device_ioctl(struct file *f, unsigned int adc_channel, unsigned long arg)
{
adcData adc;
switch(adc_channel)
{
case SET_ADC_CHANNEL:
if (copy_from_user(&adc, (adcData*)arg, sizeof(adcData)))
{
return -EFAULT;
}
adcChannel = adc.channelNumber;
break;
case GET_ADC_DATA:
switch (adcChannel)
{
case 1:
adcValue = wm97xx_read_aux_adc(wm, WM97XX_AUX_ID1);
break;
case 2:
adcValue = wm97xx_read_aux_adc(wm, WM97XX_AUX_ID2);
break;
case 3:
adcValue = wm97xx_read_aux_adc(wm, WM97XX_AUX_ID3);
break;
case 4:
adcValue = wm97xx_read_aux_adc(wm, WM97XX_AUX_ID4);
break;
default:
return -EINVAL;
break;
}
adc.channelNumber = adcChannel;
adc.adcValue = adcValue;
if (copy_to_user((adcData*)arg, &adc, sizeof(adcData)))
{
return -EFAULT;
}
printk(KERN_ALERT "AUX ADC%d reading: %d\n", adcChannel, adcValue);
break;
default:
break;
}
return 0;
}
static int sample_wm97xx_probe(struct platform_device *pdev)
{
wm = platform_get_drvdata(pdev);
if (wm == NULL)
{
printk(KERN_ALERT "Platform get drvdata returned NULL\n");
return -1;
}
return 0;
}
static int sample_wm97xx_remove(struct platform_device *pdev)
{
/* http://opensource.wolfsonmicro.com/content/using-auxadc-wm97xx-touchscreen-drivers */
return 0;
}
static struct platform_driver sample_wm97xx_driver = {
.probe  = sample_wm97xx_probe,
.remove = sample_wm97xx_remove,
.driver = {
.name = "colibri_adc",
.owner = THIS_MODULE,
},
};
static struct file_operations FileOps =
{
.owner                = THIS_MODULE,
.open                 = adc_open,
.read                 = adc_read,
.write                = adc_write,
.release              = adc_close,
.unlocked_ioctl        = adc_device_ioctl,
};
static int sample_wm97xx_init(void)
{
init_result = platform_driver_probe(&sample_wm97xx_driver, &sample_wm97xx_probe);
if (init_result < 0)
{
printk(KERN_ALERT "ADC Platform Driver probe failed with :%d\n", init_result);
return -1;
}
else
{
init_result = alloc_chrdev_region( &first, 0, 1, "adc_drv" );
if( 0 > init_result )
{
platform_driver_unregister(&sample_wm97xx_driver);
printk( KERN_ALERT "ADC Device Registration failed\n" );
return -1;
}
if ( (cl = class_create( THIS_MODULE, "chardev" ) ) == NULL )
{
platform_driver_unregister(&sample_wm97xx_driver);
printk( KERN_ALERT "ADC Class creation failed\n" );
unregister_chrdev_region( first, 1 );
return -1;
}
if( device_create( cl, NULL, first, NULL, "adc_drv" ) == NULL )
{
platform_driver_unregister(&sample_wm97xx_driver);
printk( KERN_ALERT "ADC Device creation failed\n" );
class_destroy(cl);
unregister_chrdev_region( first, 1 );
return -1;
}
cdev_init( &c_dev, &FileOps );
if( cdev_add( &c_dev, first, 1 ) == -1)
{
platform_driver_unregister(&sample_wm97xx_driver);
printk( KERN_ALERT "ADC Device addition failed\n" );
device_destroy( cl, first );
class_destroy( cl );
unregister_chrdev_region( first, 1 );
return -1;
}
return 0;
}
static void sample_wm97xx_exit(void)
{
platform_driver_unregister(&sample_wm97xx_driver);
cdev_del( &c_dev );
device_destroy( cl, first );
class_destroy( cl );
unregister_chrdev_region( first, 1 );
printk(KERN_ALERT "ADC Driver unregistered\n");
}
module_init(sample_wm97xx_init);
module_exit(sample_wm97xx_exit);
MODULE_AUTHOR("Sanchayan Maity");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Colibri T20 ADC Driver");
</code></pre>
<pre><code>
CROSS_COMPILE ?= /home/sanchayan/Toradex/gcc-linaro/bin/arm-linux-gnueabihf-
ARCH          ?= arm
SOURCE_DIR    ?= /home/sanchayan/Toradex/T20V2.0/linux-toradex
AS          = $(CROSS_COMPILE)as
LD          = $(CROSS_COMPILE)ld
CC          = $(CROSS_COMPILE)gcc
CPP         = $(CC) -E
AR          = $(CROSS_COMPILE)ar
NM          = $(CROSS_COMPILE)nm
STRIP       = $(CROSS_COMPILE)strip
OBJCOPY     = $(CROSS_COMPILE)objcopy
OBJDUMP     = $(CROSS_COMPILE)objdump
obj-m += adc_test.o
ccflags-y += -I$(SOURCE_DIR)/arch/arm
all:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(SOURCE_DIR) M=$(PWD) modules
clean:
rm *.o *.ko *.symvers *.order
</code></pre>
<p style='text-align: justify;'>The driver file and makefile source are shown above. On loading the module, the _init() will be called, which will in turn call platform_driver_register(). This will call the probe() function. If you really noticed carefully, then you can see **the driver has the same name viz. "colibri_adc" which is also the name i passed for device allocation** inside the probe() call of core driver. This is how the platform device will bind to a platform driver. They will have the same names and if you try to do a platform driver for which a platform device was not allocated, you will get a no device exists error.</p>
<p style='text-align: justify;'>One of the important things to note, is the call to **platform_set_drvdata()** in the probe call of core driver. It's not like i have traced all functions, but, from what i understand this establishes a linking between the device allocated by the core driver and your driver, which will later allow you access to the device pointer, which you need to pass to the functions in the core driver, to use them.</p>
<p style='text-align: justify;'>You can see that the functions in core driver take a pointer to struct wm97xx. From where are you suppose to get this pointer? This you will get in the probe call of the driver which we wrote, by calling **platform_get_drvdata(). **You can see that this is so, in the driver code, where it is assigned to a static global pointer. The reason for using a static global pointer is i want this pointer to be accessible in the ioctl() calls, which will be the final interface for the user, which ultimately calls the core driver functions, by passing the required wm97xx pointer. On module unloading, _exit() will be called, which in turn calls platform_driver_unregister() which will result in a call to remove() function of the driver. Now, here there is not really anything to do in remove, as we do not require any kind of memory deallocation or clean up work for the platform part of the driver. You can have a nice look at the probe and remove functions of the core driver for getting an idea of what is really done in real world driver.</p>
<p style='text-align: justify;'>So, now we finally have access to the auxiliary ADC's!!!. Hurray! And we also have our first (at least my first) platform device/driver. The user space application is below and is pretty simple.</p>
<pre><code>
#include "stdio.h"
#include "fcntl.h"
#include "linux/ioctl.h"
typedef struct
{
unsigned int channelNumber;
unsigned int adcValue;
}adcData;
#define SET_ADC_CHANNEL        _IOW('q', 1, adcData *)
#define GET_ADC_DATA         _IOR('q', 2, adcData *)
int main(void)
{
int fd;
int choice;
int adc_channel;
int retVal;
int loop = 1;
adcData adc;
fd = open( "/dev/adc_drv", O_RDWR );
if( fd < 0 )
{
printf("Cannot open device \t");
printf(" fd = %d \n",fd);
return 0;
}
while (loop)
{
printf("1: Read 2:Exit\n");
printf("Enter choice:\t");
scanf("%d", &choice);
switch(choice)
{
case 1:
printf("Enter ADC Channel Number:\t");
scanf("%d", &adc_channel);
adc.channelNumber = adc_channel;
adc.adcValue = 0;
retVal = ioctl(fd, SET_ADC_CHANNEL, &adc);
if (retVal < 0)
{
printf("Error: %d\n", retVal);
}
else
{
retVal = ioctl(fd, GET_ADC_DATA, &adc);
if (retVal < 0)
{
printf("Error: %d\n", retVal);
}
else
{
printf("ADC Channel: %d Value: %d\n", adc.channelNumber, adc.adcValue);
}
}
break;
case 2:
loop = 0;
break;
default:
break;
}
}
if( 0 != close(fd) )
{
printf("Could not close device\n");
}
return 0;
}
</code></pre>
<p style='text-align: justify;'>Do note that this is only an example, and what you might need to do will depend on what is available and what is not. Also, the code is not exactly up to the mark as per coding standards, but, i was too excited while working and rolling this out, so. I hope this clears the idea of how to use platform device/driver. Also, just in case it is not clear to people who are starting out, the core driver and header files are being changed. This will require a kernel recompilation and updating the uImage on the module, for our driver to work.</p>
<p style='text-align: justify;'>I will be putting another example of a PWM driver, which will be slightly bit more involved and should serve as another example.</p>

View File

@ -0,0 +1,462 @@
---
author: Sanchayan Maity
title: How to write a Platform Device/Driver - PWM Driver for Tegra2
tags: device driver
---
<p style='text-align: justify;'>I will get straight to the point in this tutorial and give the codes here. The explanation given in the earlier post for ADC should suffice.</p>
<p style='text-align: justify;'>Do note that this is only an example, and what you might need to do will depend on what is available and what is not. Also, the code is not exactly up to the mark as per coding standards, but, i was too excited while working and rolling this out, so. I hope this clears the idea of how to use platform device/driver. Also, just in case it is not clear to people who are starting out, the core driver and header files are being changed. This will require a kernel recompilation and updating the uImage on the module, for our driver to work.</p>
**1. Header File:**
[http://git.toradex.com/cgit/linux-toradex.git/tree/include/linux/pwm.h?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/include/linux/pwm.h?h=tegra)
**2. Core Driver File:** [http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/pwm.c?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/pwm.c?h=tegra)
**3. Board File:**
[http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra)
<p style='text-align: justify;'>Below is the change I made to the core drive file in the **tegra_pwm_probe** function.</p>
<pre><code>
platform_set_drvdata(pdev, pwm);
/*-------------------Register our PWM device---------------------*/
// Added by Sanchayan
pwm->colibripwm_dev = platform_device_alloc("colibri_pwm", -1);
if (!pwm->colibripwm_dev){
printk("PWM Device creation failed\n");
}
platform_set_drvdata(pwm->colibripwm_dev, pwm);
ret = platform_device_add(pwm->colibripwm_dev);
if (ret < 0) {
printk("PWM Device addition failed\n");
platform_device_put(pwm->colibripwm_dev);
}
/*---------------------------------------------------------------*/
mutex_lock(&pwm_lock);
</code></pre>
<p style='text-align: justify;'>In the **tegra_pwm_remove** function, i added the below line just before **return**.</p>
<pre><code>
platform_device_unregister(pwm->colibripwm_dev);
</code></pre>
<p style='text-align: justify;'>In the board file i commented out lines 737 to 830. This was done as these lines exported the PWM to led driver framework. The led driver framework only allowed controlling the PWM's duty cycle and period was fixed at 19600, as you can see in those lines. Hence, this driver to allow period or frequency to be controlled as well. Also, line 1479 was commented to prevent registration of these PWM's with the led framework.</p>
<p style='text-align: justify;'>The driver code is as follows:</p>
<pre><code>
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/init.h"
#include "linux/platform_device.h"
#include "linux/fs.h"
#include "linux/errno.h"
#include "asm/uaccess.h"
#include "linux/kdev_t.h"
#include "linux/device.h"
#include "linux/cdev.h"
#include "linux/slab.h"
#include "linux/ioctl.h"
#include "linux/pwm.h"
#define ENABLE_PWM            _IOW('q', 1, pwmData *)
#define DISABLE_PWM            _IOW('q', 2, pwmData *)
#define PWM_CHANNELS        4
typedef struct
{
unsigned int pwmChannel;
unsigned int pwmDutyCycle;
unsigned int pwmPeriod;
}pwmData;
struct pwm_device {
struct list_head    node;
struct platform_device    *pdev;
struct platform_device *colibripwm_dev;
const char        *label;
struct clk        *clk;
int            clk_enb;
void __iomem        *mmio_base;
unsigned int        in_use;
unsigned int        id;
};
struct pwm_driver
{
unsigned int period[PWM_CHANNELS];
unsigned int pwmId[PWM_CHANNELS];
struct pwm_device *pwmdev[PWM_CHANNELS];
};
static dev_t first;         // Global variable for the first device number
static struct cdev c_dev;   // Global variable for the character device structure
static struct class *cl;    // Global variable for the device class
static int init_result;
static struct pwm_driver *pwmDriver;
static ssize_t pwm_read(struct file* F, char *buf, size_t count, loff_t *f_pos)
{
return -EPERM;
}
static ssize_t pwm_write(struct file* F, const char *buf, size_t count, loff_t *f_pos)
{
return -EPERM;
}
static int pwm_open(struct inode *inode, struct file *file)
{
return 0;
}
static int pwm_close(struct inode *inode, struct file *file)
{
return 0;
}
static long pwm_device_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
int retval;
pwmData data;
switch (cmd)
{
case ENABLE_PWM:
if (copy_from_user(&data, (pwmData*)arg, sizeof(pwmData)))
{
return -EFAULT;
}
retval = pwm_config(pwmDriver->pwmdev[data.pwmChannel - 1], data.pwmDutyCycle, data.pwmPeriod);
if (retval == 0)
{
retval = pwm_enable(pwmDriver->pwmdev[data.pwmChannel - 1]);
return retval;
}
else
{
return retval;
}
break;
case DISABLE_PWM:
if (copy_from_user(&data, (pwmData*)arg, sizeof(pwmData)))
{
return -EFAULT;
}
retval = pwm_config(pwmDriver->pwmdev[data.pwmChannel - 1], 0, data.pwmPeriod);
if (retval == 0)
{
pwm_disable(pwmDriver->pwmdev[data.pwmChannel - 1]);
}
else
{
return retval;
}
break;
default:
break;
}
return 0;
}
static int pwm_device_probe(struct platform_device *pdev)
{
int i, ret = 0;
pwmDriver = kzalloc(sizeof(struct pwm_driver), GFP_KERNEL);
if (!pwmDriver)
{
printk(KERN_ALERT "Platform get drvdata returned NULL\n");
return -ENOMEM;
}
for (i = 1; i < PWM_CHANNELS; i++)
{
switch (i)
{
case 0:
pwmDriver->pwmdev[0] = pwm_request(0, "PWM_1");
break;
case 1:
pwmDriver->pwmdev[1] = pwm_request(1, "PWM_2");
break;
case 2:
pwmDriver->pwmdev[2] = pwm_request(2, "PWM_3");
break;
case 3:
pwmDriver->pwmdev[3] = pwm_request(3, "PWM_4");
break;
default:
break;
}
if (IS_ERR(pwmDriver->pwmdev[i])) {
ret = PTR_ERR(pwmDriver->pwmdev[i]);
dev_err(&pdev->dev, "unable to request PWM %d\n", i);
goto err;
}
}
platform_set_drvdata(pdev, pwmDriver);
return 0;
err:
if (i > 0) {
for (i = i - 1; i >= 1; i--) {
pwm_free(pwmDriver->pwmdev[i]);
}
}
kfree(pwmDriver);
return ret;
}
static int pwm_device_remove(struct platform_device *pdev)
{
int i;
for (i = 1; i < PWM_CHANNELS; i++) {
pwm_free(pwmDriver->pwmdev[i]);
}
printk("PWM Platform Device removed\n");
return 0;
}
static struct platform_driver pwm_driver = {
.probe = pwm_device_probe,
.remove = pwm_device_remove,
.driver = {
.name = "colibri_pwm",
.owner = THIS_MODULE,
},
};
static struct file_operations FileOps =
{
.owner                = THIS_MODULE,
.open                 = pwm_open,
.read                 = pwm_read,
.write                = pwm_write,
.release              = pwm_close,
.unlocked_ioctl       = pwm_device_ioctl,
};
static int pwm_init(void)
{
init_result = platform_driver_probe(&pwm_driver, &pwm_device_probe);
if (init_result < 0)
{
printk(KERN_ALERT "PWM Platform Driver probe failed with :%d\n", init_result);
return -1;
}
else
{
init_result = alloc_chrdev_region( &first, 0, 1, "pwm_drv" );
if( 0 > init_result )
{
platform_driver_unregister(&pwm_driver);
printk( KERN_ALERT "PWM Device Registration failed\n" );
return -1;
}
if ( (cl = class_create( THIS_MODULE, "chardev" ) ) == NULL )
{
platform_driver_unregister(&pwm_driver);
printk( KERN_ALERT "PWM Class creation failed\n" );
unregister_chrdev_region( first, 1 );
return -1;
}
if( device_create( cl, NULL, first, NULL, "pwm_drv" ) == NULL )
{
platform_driver_unregister(&pwm_driver);
printk( KERN_ALERT "PWM Device creation failed\n" );
class_destroy(cl);
unregister_chrdev_region( first, 1 );
return -1;
}
cdev_init( &c_dev, &FileOps );
if( cdev_add( &c_dev, first, 1 ) == -1)
{
platform_driver_unregister(&pwm_driver);
printk( KERN_ALERT "PWM Device addition failed\n" );
device_destroy( cl, first );
class_destroy( cl );
unregister_chrdev_region( first, 1 );
return -1;
}
}
return 0;
}
static void pwm_exit(void)
{
platform_driver_unregister(&pwm_driver);
kfree(pwmDriver);
cdev_del( &c_dev );
device_destroy( cl, first );
class_destroy( cl );
unregister_chrdev_region( first, 1 );
printk(KERN_ALERT "PWM Driver unregistered\n");
}
module_init(pwm_init);
module_exit(pwm_exit);
MODULE_AUTHOR("Sanchayan Maity");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Colibri T20 PWM Driver");
</code></pre>
The Makefile for the driver:
<pre><code>
CROSS_COMPILE ?= /home/sanchayan/Toradex/gcc-linaro/bin/arm-linux-gnueabihf-
ARCH          ?= arm
SOURCE_DIR    ?= /home/sanchayan/Toradex/T20V2.0/linux-toradex
AS          = $(CROSS_COMPILE)as
LD          = $(CROSS_COMPILE)ld
CC          = $(CROSS_COMPILE)gcc
CPP         = $(CC) -E
AR          = $(CROSS_COMPILE)ar
NM          = $(CROSS_COMPILE)nm
STRIP       = $(CROSS_COMPILE)strip
OBJCOPY     = $(CROSS_COMPILE)objcopy
OBJDUMP     = $(CROSS_COMPILE)objdump
obj-m += pwm_driver.o
ccflags-y += -I$(SOURCE_DIR)/arch/arm
all:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(SOURCE_DIR) M=$(PWD) modules
clean:
rm *.o *.ko *.symvers *.order
</code></pre>
The user space application code:
<pre><code>
#include "stdio.h"
#include "fcntl.h"
#include "linux/ioctl.h"
#define ENABLE_PWM            _IOW('q', 1, pwmData *)
#define DISABLE_PWM            _IOW('q', 2, pwmData *)
#define PWM_CHANNELS        4
typedef struct
{
unsigned int pwmChannel;
unsigned int pwmDutyCycle;
unsigned int pwmPeriod;
}pwmData;
int main(void)
{
int fd;
int choice;
int pwm_channel;
int pwm_period;
int pwm_dutycycle;
int retVal;
int loop = 1;
pwmData data;
fd = open( "/dev/pwm_drv", O_RDWR );
if( fd < 0 )
{
printf("Cannot open device \t");
printf(" fd = %d \n",fd);
return 0;
}
while (loop)
{
printf("1: Configure PWM 2: Disable PWM 3: Exit\n");
printf("Enter choice: \t");
scanf("%d", &choice);
switch(choice)
{
case 1:
printf("\nEnter PWM Channel: ");
scanf("%d", &pwm_channel);
printf("\nEnter PWM Duty Cycle: ");
scanf("%d", &pwm_dutycycle);
printf("\nEnter PWM Period: ");
scanf("%d", &pwm_period);
data.pwmChannel = pwm_channel;
data.pwmDutyCycle = pwm_dutycycle;
data.pwmPeriod = pwm_period;
retVal = ioctl(fd, ENABLE_PWM, &data);
if (retVal < 0)
{
printf("Error: %d\n", retVal);
}
else
{
printf("Return Value: %d\n", retVal);
}
break;
case 2:
printf("\nEnter PWM Channel: ");
scanf("%d", &pwm_channel);
printf("\nEnter PWM Duty Cycle: ");
scanf("%d", &pwm_dutycycle);
printf("\nEnter PWM Period: ");
scanf("%d", &pwm_period);
data.pwmChannel = pwm_channel;
data.pwmDutyCycle = pwm_dutycycle;
data.pwmPeriod = pwm_period;
retVal = ioctl(fd, DISABLE_PWM, &data);
if (retVal < 0)
{
printf("Error: %d\n", retVal);
}
else
{
printf("Return Value: %d\n", retVal);
}
break;
case 3:
loop = 0;
break;
default:
break;
}
}
if( 0 != close(fd) )
{
printf("Could not close device\n");
}
return 0;
}
</code></pre>

View File

@ -0,0 +1,23 @@
---
author: Sanchayan Maity
title: Something on Linux Graphics
tags: opengl
---
<p style='text-align: justify;'>Some good sites to learn about OpenGL. The first one is particularly good with the various explanations.</p>
1. [http://open.gl/](http://open.gl/)
2. [http://www.opengl-tutorial.org/](http://www.opengl-tutorial.org/)
3. [http://nopper.tv/norbert/opengl.html](http://nopper.tv/norbert/opengl.html)
<p style='text-align: justify;'>Good explanation at the below sites for someone looking to understand about the Linux graphics stack.</p>
1. [http://blog.mecheye.net/2012/06/the-linux-graphics-stack/](http://blog.mecheye.net/2012/06/the-linux-graphics-stack/)
2. [http://magcius.github.io/xplain/article/](http://magcius.github.io/xplain/article/)
<p style='text-align: justify;'>In case you keep up with the things going on in the Linux world and have been wondering what the hell is all this hullabaloo over X and Wayland. Read the below article on Wayland after reading the above two.</p>
[http://wayland.freedesktop.org/architecture.html](http://wayland.freedesktop.org/architecture.html)

View File

@ -0,0 +1,107 @@
---
author: Sanchayan Maity
title: Adding sysfs support to a driver
tags: device driver, sysfs
---
<p style='text-align: justify;'>A month or so back I had given an example of how to add support for a device using the platform bus framework, which as such mainly showed how exactly platform framework worked and what it was used for. In the driver I wrote, I provided access to the required values using the ioctl calls. The ioctl() calls are not a recommended way of doing this. These days drivers provide the data and control to the user space through the **sysfs** interface. As the **Linux Kernel Development** book mentions "**The sysfs file system is currently the place for implementing functionality previously reserved for ioctl() calls on device nodes or the procfs filesystem**". This post will show how to make possible what we tried to achieve in the </p>
[http://coherentmusings.wordpress.com/2013/12/13/how-to-write-a-platform-devicedriver-adc-driver-using-wm97xx-codec/](http://coherentmusings.wordpress.com/2013/12/13/how-to-write-a-platform-devicedriver-adc-driver-using-wm97xx-codec/)
<p style='text-align: justify;'>article in a much easier way using the sysfs interface. Please refer that article before reading further.</p>
<p style='text-align: justify;'>For learning about the sysfs interface and what it is, refer to the **The Linux Device Model** chapter of the **Linux Device Drivers** book or the **Devices and Modules** chapter of the **Linux Kernel Development** book by **Robert Love**.</p>
<p style='text-align: justify;'>Some other documentation can be found at the below links:</p>
1. [http://lxr.free-electrons.com/source/Documentation/driver-model/device.txt?v=3.12;a=arm](http://lxr.free-electrons.com/source/Documentation/driver-model/device.txt?v=3.12;a=arm)
2. [http://lxr.free-electrons.com/source/Documentation/filesystems/sysfs.txt](http://lxr.free-electrons.com/source/Documentation/filesystems/sysfs.txt)
<p style='text-align: justify;'>The core driver file to which we will make the changes is on the below link:</p>
[http://lxr.free-electrons.com/source/drivers/input/touchscreen/wm97xx-core.c](http://lxr.free-electrons.com/source/drivers/input/touchscreen/wm97xx-core.c)
<p style='text-align: justify;'>The below change was made to the **probe** function of the core driver file</p>
<pre><code>
if ( device_create_file(wm->dev, &dev_attr_adc_channel1) != 0 )
{
printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel1\n");
}
if ( device_create_file(wm->dev, &dev_attr_adc_channel2) != 0 )
{
printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel2\n");
}
if ( device_create_file(wm->dev, &dev_attr_adc_channel3) != 0 )
{
printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel3\n");
}
if ( device_create_file(wm->dev, &dev_attr_adc_channel4) != 0 )
{
printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel4\n");
}
</code></pre>
<p style='text-align: justify;'>The following was added to the core driver file to access the ADC data via sysfs attributes.</p>
<pre><code>
static ssize_t adc_channel1_show(struct device *child, struct device_attribute *attr, char *buf)
{
struct wm97xx *wm = dev_get_drvdata(child);
return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID1));
}
static ssize_t adc_channel2_show(struct device *child, struct device_attribute *attr, char *buf)
{
struct wm97xx *wm = dev_get_drvdata(child);
return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID2));
}
static ssize_t adc_channel3_show(struct device *child, struct device_attribute *attr, char *buf)
{
struct wm97xx *wm = dev_get_drvdata(child);
return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID3));
}
static ssize_t adc_channel4_show(struct device *child, struct device_attribute *attr, char *buf)
{
struct wm97xx *wm = dev_get_drvdata(child);
return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID4));
}
static DEVICE_ATTR(adc_channel1, 0644, adc_channel1_show, NULL);
static DEVICE_ATTR(adc_channel2, 0644, adc_channel2_show, NULL);
static DEVICE_ATTR(adc_channel3, 0644, adc_channel3_show, NULL);
static DEVICE_ATTR(adc_channel4, 0644, adc_channel4_show, NULL);
</code></pre>
<p style='text-align: justify;'>For cleanup, the attribute removal was done in the **remove** function as below.</p>
<pre><code>
device_remove_file(wm->dev, &dev_attr_adc_channel1);
device_remove_file(wm->dev, &dev_attr_adc_channel2);
device_remove_file(wm->dev, &dev_attr_adc_channel3);
device_remove_file(wm->dev, &dev_attr_adc_channel4);
</code></pre>
<p style='text-align: justify;'>After doing the above changes, the device attributes which we added show up in the sysfs tree, which can be used to get the ADC values, as shown in the below picture.</p>
[![sysfs](http://coherentmusings.files.wordpress.com/2014/02/sysfs1.png)](http://coherentmusings.files.wordpress.com/2014/02/sysfs1.png)
<p style='text-align: justify;'>If you compare this to what we tried to do previously, you can see how easy it is to get the ADC values as it allows a simple command like **cat **to access the value. Of course, you could also access the value in C code as well. Things will not always be simple like this. Here we already had a core driver, which allowed us to just add the necessary changes and get what we wanted.</p>
<p style='text-align: justify;'>Check out the below file which shows how sysfs support was added for PWM. This support landed in the 3.6 version with the following commit. </p> [http://lwn.net/Articles/553755/](http://lwn.net/Articles/553755/)
[http://lxr.free-electrons.com/source/drivers/pwm/sysfs.c?v=3.12;a=arm](http://lxr.free-electrons.com/source/drivers/pwm/sysfs.c?v=3.12;a=arm)
<p style='text-align: justify;'>As such the sysfs support will be provided through the framework being used. For example, for devices like ADC, the Industrial IO framework will be used and you will have the sysfs support available through that framework.</p>
<p style='text-align: justify;'>Hopefully my earlier post and this one will give you an idea on how to add and use the sysfs interface.</p>

View File

@ -0,0 +1,164 @@
---
author: Sanchayan Maity
title: Implementing mmap for transferring data from user space to kernel space
tags: device driver
---
<p style='text-align: justify;'>I was recently working on an application where streams from four multiplexed analog video channels had to be displayed in four windows. I was trying to do this using OpenCV while the analog video decoder/multiplexer in question was the ADV7180. For switching the channels, i was using an ioctl() call to switch the channels in a while loop with a certain time interval, while capturing the frames and putting them in a separate queues as per the channel selected. This was done in the main thread, while separate threads pulled the frames from the queue and rendered them. The capturing and rendering part was being done with OpenCV. I was not able to achieve a decent enough frame rate with this. I had put delays in certain places to avoid frame glitches in the multiple windows displaying the frames.</p>
<p style='text-align: justify;'>Thinking that may be the ioctl() call and the context switch is the reason i have to use delays and this is going slow, i decided to look into ways of transferring data faster between the user and kernel space, instead of using an ioctl() call. An mmap() implementation in a driver or a memory mapping between user and kernel space is the fastest way to transfer data. This approach doesn't incur a context switch nor a memory buffer copying. Below is a sample code showing how a mmap() implementation for a driver is done.</p>
<p style='text-align: justify;'>Below is the driver code.</p>
<pre><code>
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/init.h"
#include "linux/fs.h"
#include "linux/debugfs.h"
#include "linux/slab.h"
#include "linux/mm.h"
#ifndef VM_RESERVED
# define  VM_RESERVED   (VM_DONTEXPAND | VM_DONTDUMP)
#endif
struct dentry  *file;
struct mmap_info
{
    char *data;            
    int reference;      
};
void mmap_open(struct vm_area_struct *vma)
{
    struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
    info->reference++;
}
void mmap_close(struct vm_area_struct *vma)
{
    struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
    info->reference--;
}
static int mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    struct page *page;
    struct mmap_info *info;    
    
    info = (struct mmap_info *)vma->vm_private_data;
    if (!info->data)
    {
        printk("No data\n");
        return 0;    
    }
    
    page = virt_to_page(info->data);    
    
    get_page(page);
    vmf->page = page;            
    
    return 0;
}
struct vm_operations_struct mmap_vm_ops =
{
    .open =     mmap_open,
    .close =    mmap_close,
    .fault =    mmap_fault,    
};
int op_mmap(struct file *filp, struct vm_area_struct *vma)
{
    vma->vm_ops = &mmap_vm_ops;
    vma->vm_flags |= VM_RESERVED;    
    vma->vm_private_data = filp->private_data;
    mmap_open(vma);
    return 0;
}
int mmapfop_close(struct inode *inode, struct file *filp)
{
    struct mmap_info *info = filp->private_data;
    
    free_page((unsigned long)info->data);
    kfree(info);
    filp->private_data = NULL;
    return 0;
}
int mmapfop_open(struct inode *inode, struct file *filp)
{
    struct mmap_info *info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);    
    info->data = (char *)get_zeroed_page(GFP_KERNEL);
    memcpy(info->data, "hello from kernel this is file: ", 32);
    memcpy(info->data + 32, filp->f_dentry->d_name.name, strlen(filp->f_dentry->d_name.name));
    /* assign this info struct to the file */
    filp->private_data = info;
    return 0;
}
static const struct file_operations mmap_fops = {
    .open = mmapfop_open,
    .release = mmapfop_close,
    .mmap = op_mmap,
};
static int __init mmapexample_module_init(void)
{
    file = debugfs_create_file("mmap_example", 0644, NULL, NULL, &mmap_fops);
    return 0;
}
static void __exit mmapexample_module_exit(void)
{
    debugfs_remove(file);
}
module_init(mmapexample_module_init);
module_exit(mmapexample_module_exit);
MODULE_LICENSE("GPL");
</code></pre>
Below is the user space application showing it's use in an application.
<pre><code>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#define PAGE_SIZE     4096
int main ( int argc, char **argv )
{
    int configfd;
    char * address = NULL;
    configfd = open("/sys/kernel/debug/mmap_example", O_RDWR);
    if(configfd < 0)
    {
        perror("Open call failed");
        return -1;
    }
    
    address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, configfd, 0);
    if (address == MAP_FAILED)
    {
        perror("mmap operation failed");
        return -1;
    }
    printf("Initial message: %s\n", address);
    memcpy(address + 11 , "*user*", 6);
    printf("Changed message: %s\n", address);
    close(configfd);    
    return 0;
}
</code></pre>
<p style='text-align: justify;'>The above code should run well for both desktop or embedded Linux. For learning more, refer to Chapter 15 of the Linux Device Drivers book.</p>
<p style='text-align: justify;'>Ultimately though, I didn't do a mmap() but decided to do the switching channel work in the driver itself using kernel timers and workqueues. Not that this improved the frame rate much either, may be a 1 frame per second improvement. May be someone will give me some feedback some day with this task. Till then, hope you find this post useful.</p>

View File

@ -0,0 +1,404 @@
---
author: Sanchayan Maity
title: Extracting frame from a gstreamer pipeline and displaying it with OpenCV
tags: opencv, gstreamer
---
<p style='text-align: justify;'>Not much to write or say in this post. I was trying to extract a frame from the gstreamer pipeline and then display it with OpenCV.</p>
<p style='text-align: justify;'>There are two approaches in the code below.</p>
<p style='text-align: justify;'>1. Register a callback function whenever a new buffer becomes available with appsink and then use a locking mechanism to synchronize the extraction of the frame and display in the main thread.</p>
<p style='text-align: justify;'>2. The second one is to extract the buffer yourself in a while loop in the main thread.</p>
<p style='text-align: justify;'>The first one is active in the code below and the second one commented out. To enable the first mechanism, uncomment the mutex locking and signal connect mechanism and comment out the pull buffer call related stuff in the while loop.</p>
<p style='text-align: justify;'>Learn more about gstreamer from [http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/index.html](http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/index.html) and especially refer section 19.</p>
<p style='text-align: justify;'>For some reason, I am experiencing a memory leak issue with the below code (more so with the fist approach) and haven't got around and being able to fix it. Also, for your platform the gstreamer pipeline elements will be different. Another problem was, I get x-raw-yuv data from my gstreamer source element and I am only able to display the black and white image with OpenCV. Nonetheless, I thought this might be useful and may be someone can also point out the error to me. Not a gstreamer expert by any means.</p>
<pre><code>
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv/cv.h"
#include "gstreamer-0.10/gst/gst.h"
#include "gstreamer-0.10/gst/gstelement.h"
#include "gstreamer-0.10/gst/app/gstappsink.h"
#include "iostream"
#include "stdio.h"
#include "unistd.h"
#include "pthread.h"
#include "X11/Xlib.h"
#include "X11/Xutil.h"
using namespace std;
using namespace cv;
/* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData
{
    GstElement *appsink;
    GstElement *colorSpace;    
    GstElement *pipeline;
    GstElement *vsource_capsfilter, *mixercsp_capsfilter, *cspappsink_capsfilter;
    GstElement *mixer_capsfilter;
    GstElement *bin_capture;
    GstElement *video_source, *deinterlace;     
    GstElement *nv_video_mixer;    
    GstPad *pad;
    GstCaps *srcdeinterlace_caps, *mixercsp_caps, *cspappsink_caps;    
    GstBus *bus;
    GstMessage *msg;        
}gstData;
GstBuffer* buffer;        
pthread_mutex_t threadMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t waitForGstBuffer = PTHREAD_COND_INITIALIZER;
/* Global variables */
CascadeClassifier face_cascade;
IplImage *frame = NULL;     
string window_name =         "Toradex Face Detection Demo";
String face_cascade_name =    "/home/root/haarcascade_frontalface_alt2.xml";
const int BORDER =             8;          // Border between GUI elements to the edge of the image.
template <typename T> string toString(T t)
{
    ostringstream out;
    out << t;
    return out.str();
}
// Draw text into an image. Defaults to top-left-justified text, but you can give negative x coords for right-justified text,
// and/or negative y coords for bottom-justified text
// Returns the bounding rect around the drawn text
Rect drawString(Mat img, string text, Point coord, Scalar color, float fontScale = 0.6f, int thickness = 1, int fontFace = FONT_HERSHEY_COMPLEX)
{
    // Get the text size & baseline.
    int baseline = 0;
    Size textSize = getTextSize(text, fontFace, fontScale, thickness, &baseline);
    baseline += thickness;
    // Adjust the coords for left/right-justified or top/bottom-justified.
    if (coord.y >= 0) {
        // Coordinates are for the top-left corner of the text from the top-left of the image, so move down by one row.
        coord.y += textSize.height;
    }
    else {
        // Coordinates are for the bottom-left corner of the text from the bottom-left of the image, so come up from the bottom.
        coord.y += img.rows - baseline + 1;
    }
    // Become right-justified if desired.
    if (coord.x < 0) {
        coord.x += img.cols - textSize.width + 1;
    }
    // Get the bounding box around the text.
    Rect boundingRect = Rect(coord.x, coord.y - textSize.height, textSize.width, baseline + textSize.height);
    // Draw anti-aliased text.
    putText(img, text, coord, fontFace, fontScale, color, thickness, CV_AA);
    // Let the user know how big their text is, in case they want to arrange things.
    return boundingRect;
}
void create_pipeline(gstData *data)
{
    data->pipeline = gst_pipeline_new ("pipeline");
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
}
gboolean CaptureGstBuffer(GstAppSink *sink, gstData *data)
{            
    //g_signal_emit_by_name (sink, "pull-buffer", &buffer);
    pthread_mutex_lock(&threadMutex);
    buffer = gst_app_sink_pull_buffer(sink);
    if (buffer)
    {        
        frame = cvCreateImage(cvSize(720, 576), IPL_DEPTH_16U, 3);
        if (frame == NULL)
        {
            g_printerr("IplImageFrame is null.\n");
        }
        else
        {
            //buffer = gst_app_sink_pull_buffer(sink);
            frame->imageData = (char*)GST_BUFFER_DATA(buffer);        
            if (frame->imageData == NULL)
            {
                g_printerr("IplImage data is null.\n");        
            }
        }        
        pthread_cond_signal(&waitForGstBuffer);            
    }            
    pthread_mutex_unlock(&threadMutex);
    return TRUE;
}
gboolean init_video_capture(gstData *data)
{    
    data->video_source = gst_element_factory_make("v4l2src", "video_source_live");
    data->vsource_capsfilter = gst_element_factory_make ("capsfilter", "vsource_cptr_capsfilter");
    data->deinterlace = gst_element_factory_make("deinterlace", "deinterlace_live");
    data->nv_video_mixer = gst_element_factory_make("nv_omx_videomixer", "nv_video_mixer_capture");    
    data->mixercsp_capsfilter = gst_element_factory_make ("capsfilter", "mixercsp_capsfilter");
    data->colorSpace = gst_element_factory_make("ffmpegcolorspace", "csp");        
    data->cspappsink_capsfilter = gst_element_factory_make ("capsfilter", "cspappsink_capsfilter");
    data->appsink = gst_element_factory_make("appsink", "asink");
        
    if (!data->video_source || !data->vsource_capsfilter || !data->deinterlace || !data->nv_video_mixer || !data->mixercsp_capsfilter || !data->appsink \
        || !data->colorSpace || !data->cspappsink_capsfilter)
    {
        g_printerr ("Not all elements for video were created.\n");
        return FALSE;
    }        
    
    g_signal_connect( data->pipeline, "deep-notify", G_CALLBACK( gst_object_default_deep_notify ), NULL );        
    
    gst_app_sink_set_emit_signals((GstAppSink*)data->appsink, true);
    gst_app_sink_set_drop((GstAppSink*)data->appsink, true);
    gst_app_sink_set_max_buffers((GstAppSink*)data->appsink, 1);    
    
    data->srcdeinterlace_caps = gst_caps_from_string("video/x-raw-yuv, width=(int)720, height=(int)576, format=(fourcc)I420, framerate=(fraction)1/1");        
    if (!data->srcdeinterlace_caps)
        g_printerr("1. Could not create media format string.\n");        
    g_object_set (G_OBJECT (data->vsource_capsfilter), "caps", data->srcdeinterlace_caps, NULL);
    gst_caps_unref(data->srcdeinterlace_caps);        
    
    data->mixercsp_caps = gst_caps_from_string("video/x-raw-yuv, width=(int)720, height=(int)576, format=(fourcc)I420, framerate=(fraction)1/1, pixel-aspect-ratio=(fraction)1/1");    
    if (!data->mixercsp_caps)
        g_printerr("2. Could not create media format string.\n");        
    g_object_set (G_OBJECT (data->mixercsp_capsfilter), "caps", data->mixercsp_caps, NULL);
    gst_caps_unref(data->mixercsp_caps);    
    
    data->cspappsink_caps = gst_caps_from_string("video/x-raw-yuv, width=(int)720, height=(int)576, format=(fourcc)I420, framerate=(fraction)1/1");        
    if (!data->cspappsink_caps)
        g_printerr("3. Could not create media format string.\n");        
    g_object_set (G_OBJECT (data->cspappsink_capsfilter), "caps", data->cspappsink_caps, NULL);    
    gst_caps_unref(data->cspappsink_caps);        
            
    data->bin_capture = gst_bin_new ("bin_capture");        
    
    /*if(g_signal_connect(data->appsink, "new-buffer", G_CALLBACK(CaptureGstBuffer), NULL) <= 0)
    {
        g_printerr("Could not connect signal handler.\n");
        exit(1);
    }*/
    
    gst_bin_add_many (GST_BIN (data->bin_capture), data->video_source, data->vsource_capsfilter, data->deinterlace, data->nv_video_mixer, \
                        data->mixercsp_capsfilter, data->colorSpace, data->cspappsink_capsfilter, data->appsink, NULL);
    
    if (gst_element_link_many(data->video_source, data->vsource_capsfilter, data->deinterlace, NULL) != TRUE)
    {
        g_printerr ("video_src to deinterlace not linked.\n");
        return FALSE;
    }        
    
    if (gst_element_link_many (data->deinterlace, data->nv_video_mixer, NULL) != TRUE)
    {
        g_printerr ("deinterlace to video_mixer not linked.\n");
        return FALSE;
    }        
    
    if (gst_element_link_many (data->nv_video_mixer, data->mixercsp_capsfilter, data->colorSpace, NULL) != TRUE)
    {
        g_printerr ("video_mixer to colorspace not linked.\n");
        return FALSE;    
    }
    
    if (gst_element_link_many (data->colorSpace, data->appsink, NULL) != TRUE)
    {
        g_printerr ("colorspace to appsink not linked.\n");
        return FALSE;    
    }
    
    cout << "Returns from init_video_capture." << endl;
    return TRUE;
}
void delete_pipeline(gstData *data)
{
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    g_print ("Pipeline set to NULL\n");
    gst_object_unref (data->bus);
    gst_object_unref (data->pipeline);
    g_print ("Pipeline deleted\n");
}
gboolean add_bin_capture_to_pipe(gstData *data)
{
    if((gst_bin_add(GST_BIN (data->pipeline), data->bin_capture)) != TRUE)
    {
        g_print("bin_capture not added to pipeline\n");
    }
    
    if(gst_element_set_state (data->pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS)
    {        
        return TRUE;
    }
    else
    {
        cout << "Failed to set pipeline state to NULL." << endl;
        return FALSE;        
    }
}
gboolean remove_bin_capture_from_pipe(gstData *data)
{
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    gst_element_set_state (data->bin_capture, GST_STATE_NULL);
    if((gst_bin_remove(GST_BIN (data->pipeline), data->bin_capture)) != TRUE)
    {
        g_print("bin_capture not removed from pipeline\n");
    }    
    return TRUE;
}
gboolean start_capture_pipe(gstData *data)
{
    if(gst_element_set_state (data->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS)
        return TRUE;
    else
    {
        cout << "Failed to set pipeline state to PLAYING." << endl;
        return FALSE;
    }
}
gboolean stop_capture_pipe(gstData *data)
{
    gst_element_set_state (data->bin_capture, GST_STATE_NULL);
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    return TRUE;
}
gboolean deinit_video_live(gstData *data)
{
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    gst_element_set_state (data->bin_capture, GST_STATE_NULL);
    gst_object_unref (data->bin_capture);
    return TRUE;
}
gboolean check_bus_cb(gstData *data)
{
    GError *err = NULL;                
    gchar *dbg = NULL;   
          
    g_print("Got message: %s\n", GST_MESSAGE_TYPE_NAME(data->msg));
    switch(GST_MESSAGE_TYPE (data->msg))
    {
        case GST_MESSAGE_EOS:       
            g_print ("END OF STREAM... \n");
            break;
        case GST_MESSAGE_ERROR:
            gst_message_parse_error (data->msg, &err, &dbg);
            if (err)
            {
                g_printerr ("ERROR: %s\n", err->message);
                g_error_free (err);
            }
            if (dbg)
            {
                g_printerr ("[Debug details: %s]\n", dbg);
                g_free (dbg);
            }
            break;
        default:
            g_printerr ("Unexpected message of type %d", GST_MESSAGE_TYPE (data->msg));
            break;
    }
    return TRUE;
}
void get_pipeline_bus(gstData *data)
{
    data->bus = gst_element_get_bus (data->pipeline);
    data->msg = gst_bus_poll (data->bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
    if(GST_MESSAGE_TYPE (data->msg))
    {
        check_bus_cb(data);
    }
    gst_message_unref (data->msg);
}
int main(int argc, char *argv[])
{        
    //Mat frame;
    VideoCapture capture;    
    gstData gstreamerData;
    GstBuffer *gstImageBuffer;
    
    //XInitThreads();
    gst_init (&argc, &argv);
    create_pipeline(&gstreamerData);
    if(init_video_capture(&gstreamerData))
    {        
        add_bin_capture_to_pipe(&gstreamerData);    
        start_capture_pipe(&gstreamerData);
        //get_pipeline_bus(&gstreamerData);    
    
        cout << "Starting while loop..." << endl;
        cvNamedWindow("Toradex Face Detection Demo with Gstreamer", 0);    
    
        while(true)
        {    
            //pthread_mutex_lock(&threadMutex);
            //pthread_cond_wait(&waitForGstBuffer, &threadMutex);
            
            gstImageBuffer = gst_app_sink_pull_buffer((GstAppSink*)gstreamerData.appsink);
        
            if (gstImageBuffer != NULL)
            {        
                frame = cvCreateImage(cvSize(720, 576), IPL_DEPTH_8U, 1);
                    
                if (frame == NULL)
                {
                    g_printerr("IplImageFrame is null.\n");
                }
                else
                {        
                    frame->imageData = (char*)GST_BUFFER_DATA(gstImageBuffer);        
                    if (frame->imageData == NULL)
                    {
                        g_printerr("IplImage data is null.\n");            
                    }                    
                    cvShowImage("Toradex Face Detection Demo with Gstreamer", frame);  
                    cvWaitKey(1);                    
                    gst_buffer_unref(gstImageBuffer);
                }
            }
            else
            {
                cout << "Appsink buffer didn't return buffer." << endl;
            }
            /*
            if (frame)
            {
                cvShowImage("Toradex Face Detection Demo with Gstreamer", frame);
            }
            gst_buffer_unref(buffer);
            buffer = NULL;            
            pthread_mutex_unlock(&threadMutex);    
            cvWaitKey(1);*/                                    
        }
    }
    else
    {
        exit(1);
    }
              
    //Destroy the window
    cvDestroyWindow("Toradex Face Detection Demo with Gstreamer");
       remove_bin_capture_from_pipe(&gstreamerData);
       deinit_video_live(&gstreamerData);    
    delete_pipeline(&gstreamerData);
    
       return 0;
}</code></pre>

View File

@ -0,0 +1,23 @@
---
author: Sanchayan Maity
title: Multithreaded Facial Recognition with OpenCV
tags: opencv
---
<p style='text-align: justify;'>It has been quiet a while since i have been maintaining this blog and giving some information and codes to work with. Lately i started noticing that this can become tedious. So from now on i will try to give access to the projects or work i do using git. I had a git account since September of 2013, but, never got around to using it.</p>
<p style='text-align: justify;'>This project is a modification of the facial recognition project which is given with Mastering OpenCV with Practical Computer Vision. The book is available with Packtpub and Amazon. The code base is here [https://github.com/MasteringOpenCV](https://github.com/MasteringOpenCV) and is maintained by Shervin Emami.</p>
<p style='text-align: justify;'>I was trying to do the same on a Toradex NVidia Tegra3 based Colibri T30 module which has four CPU cores. The code is single threaded and as such doesn't detect faces if the training process is going on. I made changes to this, so that even while the training process is on going, it can still detect faces. And mind you, the training process can go on for quiet a while if there are more than 3-4 faces. So, this is basically a two threaded version of the main code along with a few more changes as per my personal requirement. You can actually go one step further to utilize three cores, though right now i can't recall what was suppose to be the job of the third core.</p>
<p style='text-align: justify;'>I do apologize for the code not being very clean. At first i was trying to use the threading facility available with C++, but since i am no C++ expert i ran into problems which i wasn't able to fix quickly. Decided to use pthreads, which i am much more familiar and comfortable with. You will find the C++ threading part which i was trying to do, commented out. Once i get some C++ mastery using Bruce Eckel's Thinking in C++, i will try to do it cleanly in just C++ and clean it or clean it anyways when i get time.</p>
<p style='text-align: justify;'>You can clone the project with:</p>
<pre><code>
git clone https://github.com/SanchayanMaity/MultithreadedFaceRecognition.git
</code></pre>
<p style='text-align: justify;'>You need to modify the Makefile to compile the project and use it on your platform which can be a PC or an embedded board. Please do note that this project will be useful only if you are running this on a platform which has two cores or more.</p>
<p style='text-align: justify;'>Hope you guys find it useful. Cheers! And git and Linus are awesome.</p>

View File

@ -0,0 +1,367 @@
---
author: Sanchayan Maity
title: Playing wav/mp3 file using gstreamer in code
tags: gstreamer
---
<p style='text-align: justify;'>You can also clone this with</p>
<pre><code>
git clone https://github.com/SanchayanMaity/gstreamer-audio-playback.git
</pre></code>
<p style='text-align: justify;'>Though I used this on a Toradex Colibri Vybrid module, you can use the same on a Beagleboard or desktop with the correct setup.</p>
<pre><code>
/*
Notes for compilation:
1. For compiling the code along with the Makefile given, a OE setup is mandatory.
2. Before compiling, change the paths as per the setup of your environment.
Please refer the Gstreamer Application Development Manual at the below link before proceeding further
http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/index.html
Comprehensive documentation for Gstreamer
http://gstreamer.freedesktop.org/documentation/
The following elements/plugins/packages are expected to be in the module image for this to work
gstreamer
gst-plugins-base
gst-plugins-good-wavparse
gst-plugins-good-alsa
gst-plugins-good-audioconvert
gst-plugins-ugly-mad
Pipeline to play .wav audio file from command line
gst-launch filesrc location="location of file" ! wavparse ! alsasink
Pipeline to play .mp3 audio file from command line
gst-launch filesrc location="location of file" ! mad ! audioconvert ! alsasink
It is also assumed that the USB to Audio device is the only audio device being used on the system, if not the
"device" parameter for alsasink will change and the parameter to be used needs to be checked with cat /proc/asound/cards,
which then needs to be set as follows
In gstreamer pipeline
Pipeline to play .wav audio file from command line
gst-launch filesrc location="location of file" ! wavparse ! alsasink device=hw:1,0
Pipeline to play .mp3 audio file from command line
gst-launch filesrc location="location of file" ! mad ! audioconvert ! alsasink device=hw:1,0
In code initialisation in init_audio_playback_pipeline
g_object_set (G_OBJECT (data->alsasink), "device", "hw:0,0", NULL);
                            OR
g_object_set (G_OBJECT (data->alsasink), "device", "hw:1,0", NULL);
The pipeline will ideally remain the same for a different audio device, only the device parameter for alsasink will change
*/
#include <gstreamer-0.10/gst/gst.h>
#include <gstreamer-0.10/gst/gstelement.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define NUMBER_OF_BYTES_FOR_FILE_LOCATION    256
volatile gboolean exit_flag = FALSE;
typedef struct  
{
    GstElement *file_source;
    GstElement *pipeline;
    GstElement *audio_decoder;    
    GstElement *audioconvert;
    GstElement *alsasink;    
    GstElement *bin_playback;    
    GstBus *bus;
    GstMessage *message;        
    gchar filelocation[NUMBER_OF_BYTES_FOR_FILE_LOCATION];
}gstData;
gstData gstreamerData;
// Create the pipeline element
gboolean create_pipeline(gstData *data)
{        
    data->pipeline = gst_pipeline_new("audio_pipeline");    
    if (data->pipeline == NULL)
    {            
        return FALSE;
    }
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    return TRUE;
}
// Callback function for dynamically linking the "wavparse" element and "alsasink" element
void on_pad_added (GstElement *src_element, GstPad *src_pad, gpointer data)
{
    g_print ("\nLinking dynamic pad between wavparse and alsasink\n");
    GstElement *sink_element = (GstElement *) data;     // Is alsasink
    GstPad *sink_pad = gst_element_get_static_pad (sink_element, "sink");
    gst_pad_link (src_pad, sink_pad);
    gst_object_unref (sink_pad);
    src_element = NULL;     // Prevent "unused" warning here
}
// Setup the pipeline
gboolean init_audio_playback_pipeline(gstData *data)
{
    if (data == NULL)
        return FALSE;
        
    data->file_source = gst_element_factory_make("filesrc", "filesource");    
    
    if (strstr(data->filelocation, ".mp3"))
    {
        g_print ("\nMP3 Audio decoder selected\n");
        data->audio_decoder = gst_element_factory_make("mad", "audiomp3decoder");
    }
    
    if (strstr(data->filelocation, ".wav"))
    {
        g_print ("\nWAV Audio decoder selected\n");
        data->audio_decoder = gst_element_factory_make("wavparse", "audiowavdecoder");
    }
        
    data->audioconvert = gst_element_factory_make("audioconvert", "audioconverter");    
    
    data->alsasink = gst_element_factory_make("alsasink", "audiosink");
    
    if ( !data->file_source || !data->audio_decoder || !data->audioconvert || !data->alsasink )
    {
        g_printerr ("\nNot all elements for audio pipeline were created\n");
        return FALSE;
    }    
    
    // Uncomment this if you want to see some debugging info
    //g_signal_connect( data->pipeline, "deep-notify", G_CALLBACK( gst_object_default_deep_notify ), NULL );    
    
    g_print("\nFile location: %s\n", data->filelocation);
    g_object_set (G_OBJECT (data->file_source), "location", data->filelocation, NULL);            
    
    data->bin_playback = gst_bin_new ("bin_playback");    
    
    if (strstr(data->filelocation, ".mp3"))
    {
        gst_bin_add_many(GST_BIN(data->bin_playback), data->file_source, data->audio_decoder, data->audioconvert, data->alsasink, NULL);
    
        if (gst_element_link_many (data->file_source, data->audio_decoder, NULL) != TRUE)
        {
            g_printerr("\nFile source and audio decoder element could not link\n");
            return FALSE;
        }
    
        if (gst_element_link_many (data->audio_decoder, data->audioconvert, NULL) != TRUE)
        {
            g_printerr("\nAudio decoder and audio converter element could not link\n");
            return FALSE;
        }
    
        if (gst_element_link_many (data->audioconvert, data->alsasink, NULL) != TRUE)
        {
            g_printerr("\nAudio converter and audio sink element could not link\n");
            return FALSE;
        }
    }
    
    if (strstr(data->filelocation, ".wav"))
    {
        gst_bin_add_many(GST_BIN(data->bin_playback), data->file_source, data->audio_decoder, data->alsasink, NULL);
    
        if (gst_element_link_many (data->file_source, data->audio_decoder, NULL) != TRUE)
        {
            g_printerr("\nFile source and audio decoder element could not link\n");
            return FALSE;
        }
    
        // Avoid checking of return value for linking of "wavparse" element and "alsasink" element
        // Refer http://stackoverflow.com/questions/3656051/unable-to-play-wav-file-using-gstreamer-apis
        
        gst_element_link_many (data->audio_decoder, data->alsasink, NULL);
        
        g_signal_connect(data->audio_decoder, "pad-added", G_CALLBACK(on_pad_added), data->alsasink);    
    }    
    
    return TRUE;
}
// Starts the pipeline
gboolean start_playback_pipe(gstData *data)
{
    // http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstElement.html#gst-element-set-state
    gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
    while(gst_element_get_state(data->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS);    
    return TRUE;
}
// Add the pipeline to the bin
gboolean add_bin_playback_to_pipe(gstData *data)
{
    if((gst_bin_add(GST_BIN (data->pipeline), data->bin_playback)) != TRUE)
    {
        g_print("\nbin_playback not added to pipeline\n");
        return FALSE;    
    }
    
    if(gst_element_set_state (data->pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS)
    {        
        return TRUE;
    }
    else
    {
        g_print("\nFailed to set pipeline state to NULL\n");
        return FALSE;        
    }
}
// Disconnect the pipeline and the bin
void remove_bin_playback_from_pipe(gstData *data)
{
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    gst_element_set_state (data->bin_playback, GST_STATE_NULL);
    if((gst_bin_remove(GST_BIN (data->pipeline), data->bin_playback)) != TRUE)
    {
        g_print("\nbin_playback not removed from pipeline\n");
    }    
}
// Cleanup
void delete_pipeline(gstData *data)
{
    if (data->pipeline)
        gst_element_set_state (data->pipeline, GST_STATE_NULL);    
    if (data->bus)
        gst_object_unref (data->bus);
    if (data->pipeline)
        gst_object_unref (data->pipeline);    
}
// Function for checking the specific message on bus
// We look for EOS or Error messages
gboolean check_bus_cb(gstData *data)
{
    GError *err = NULL;                
    gchar *dbg = NULL;   
          
    g_print("\nGot message: %s\n", GST_MESSAGE_TYPE_NAME(data->message));
    switch(GST_MESSAGE_TYPE (data->message))
    {
        case GST_MESSAGE_EOS:       
            g_print ("\nEnd of stream... \n\n");
            exit_flag = TRUE;
            break;
        case GST_MESSAGE_ERROR:
            gst_message_parse_error (data->message, &err, &dbg);
            if (err)
            {
                g_printerr ("\nERROR: %s\n", err->message);
                g_error_free (err);
            }
            if (dbg)
            {
                g_printerr ("\nDebug details: %s\n", dbg);
                g_free (dbg);
            }
            exit_flag = TRUE;
            break;
        default:
            g_printerr ("\nUnexpected message of type %d\n", GST_MESSAGE_TYPE (data->message));
            break;
    }
    return TRUE;
}
int main(int argc, char *argv[])
{    
    if (argc != 2)
    {
        g_print("\nUsage: ./audiovf /home/root/filename.mp3\n");
        g_print("Usage: ./audiovf /home/root/filename.wav\n");
        g_print("Note: Number of bytes for file location: %d\n\n", NUMBER_OF_BYTES_FOR_FILE_LOCATION);
        return FALSE;
    }
    
    if ((!strstr(argv[1], ".mp3")) && (!strstr(argv[1], ".wav")))
    {
        g_print("\nOnly mp3 & wav files can be played\n");
        g_print("Specify the mp3 or wav file to be played\n");
        g_print("Usage: ./audiovf /home/root/filename.mp3\n");
        g_print("Usage: ./audiovf /home/root/filename.wav\n");
        g_print("Note: Number of bytes for file location: %d\n\n", NUMBER_OF_BYTES_FOR_FILE_LOCATION);
        return FALSE;
    }    
    
    // Initialise gstreamer. Mandatory first call before using any other gstreamer functionality
    gst_init (&argc, &argv);
    
    memset(gstreamerData.filelocation, 0, sizeof(gstreamerData.filelocation));
    strcpy(gstreamerData.filelocation, argv[1]);        
    
    if (!create_pipeline(&gstreamerData))
        goto err;        
    
    if(init_audio_playback_pipeline(&gstreamerData))
    {    
        if(!add_bin_playback_to_pipe(&gstreamerData))
            goto err;        
        
        if(start_playback_pipe(&gstreamerData))
        {
            gstreamerData.bus = gst_element_get_bus (gstreamerData.pipeline);
            
            while (TRUE)
            {
                if (gstreamerData.bus)
                {    
                    // Check for End Of Stream or error messages on bus
                    // The global exit_flag will be set in case of EOS or error. Exit if the flag is set
                    gstreamerData.message = gst_bus_poll (gstreamerData.bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
                    if(GST_MESSAGE_TYPE (gstreamerData.message))
                    {
                        check_bus_cb(&gstreamerData);
                    }
                    gst_message_unref (gstreamerData.message);            
                }            
                
                if (exit_flag)
                    break;            
                
                sleep(1);                
            }                    
        }    
        remove_bin_playback_from_pipe(&gstreamerData);                    
    }    
err:    
    delete_pipeline(&gstreamerData);
    
    return TRUE;
}
</code></pre>
<p style='text-align: justify;'>A simple Makefile for compiling the code. You need to change the path as per your OE setup.</p>
<pre><code>
#Notes for compilation:
#1. For compiling the code with this Makefile, a OE setup is mandatory.
#2. Before compiling, change the paths as per the setup of your environment.
CC = ${HOME}/oe-core/build/out-eglibc/sysroots/x86_64-linux/usr/bin/armv7ahf-vfp-neon-angstrom-linux-gnueabi/arm-angstrom-linux-gnueabi-gcc
INCLUDES = "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include/glib-2.0" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/lib/glib-2.0/include" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include/gstreamer-0.10" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include/libxml2"
LIB_PATH = "-L${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/lib"
LDFLAGS = -lpthread -lgobject-2.0 -lglib-2.0 -lgstreamer-0.10 -lgstapp-0.10
CFLAGS = -O3 -g --sysroot=${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf
all:
    ${CC} ${CFLAGS} ${INCLUDES} ${LIB_PATH} ${LDFLAGS} -o audiovf audiovf.c
clean:
    rm -rf audiovf
</code></pre>

View File

@ -0,0 +1,186 @@
---
author: Sanchayan Maity
title: Setting up Yocto/Poky for Beagleboard-xM/Beagleboard/Beaglebone
tags: yocto, poky, beagleboard, qt5
---
<p style='text-align: justify;'>I have used Buildroot before for setting up environment for Beagleboard, but OpenEmbedded or Yocto gives much more power with regard to the number of packages you can build or do your own customizations. From here on i am assuming that you have a separate beagle directory in which you are doing this. A knowledge of git and your smartness ;-) is assumed.</p>
Clone the poky repository with git
<pre><code>
git clone git://git.yoctoproject.org/poky
</code></pre>
<p style='text-align: justify;'>Enter the poky directory and clone the "meta-ti" layer. This layer will be required for Beagle specific builds.</p>
<pre><code>
git clone git://git.yoctoproject.org/meta-ti meta-ti
</code></pre>
<p style='text-align: justify;'>Clone the meta-openembedded, openembedded-core and meta-qt5 layers, while in the poky directory</p>
<pre><code>
git clone git://git.openembedded.org/openembedded-core openembedded-core
</code></pre>
<pre><code>
git clone git://git.openembedded.org/meta-openembedded meta-openembedded
</code></pre>
<pre><code>
git clone git://github.com/meta-qt5/meta-qt5 meta-qt5
</code></pre>
<p style='text-align: justify;'>In each of the git cloned repositories, select the branch you want to work with. If you do not select a branch, all of them will be with the default **master** branch. For example, you can select the **dora** or **daisy** branch.</p>
<p style='text-align: justify;'>While in the beagle directory, run **source poky/oe-init-build-env poky-build** . The **poky-build** directory is where all your build will take place, downloads will happen and all the packages and images will reside.</p>
<p style='text-align: justify;'>So, now your beagle directory will have two directories inside **poky, poky-build**. The poky directory will have the various meta layers.</p>
<p style='text-align: justify;'>After running the **source** command above, do not exit the terminal or switch to a different terminal or directory. The script which was run did the necessary task of setting up the environment variables required for the build.</p>
<p style='text-align: justify;'>Open the **conf/bblayers.conf** file with an editor like nano or gedit. Add the required entries to have this file exactly as below.</p>
<pre><code>
# LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
LCONF_VERSION = "6"
BBPATH = "${TOPDIR}"
BBFILES ?= ""
BBLAYERS ?= " \
  /home/sanchayan/beagle/poky/meta \
  /home/sanchayan/beagle/poky/meta-yocto \
  /home/sanchayan/beagle/poky/meta-yocto-bsp \
  /home/sanchayan/beagle/poky/meta-ti \
  /home/sanchayan/beagle/poky/meta-qt5 \
  /home/sanchayan/beagle/poky/openembedded-core \
  /home/sanchayan/beagle/poky/meta-openembedded/meta-ruby \
  /home/sanchayan/beagle/poky/meta-openembedded/meta-oe \
  "
BBLAYERS_NON_REMOVABLE ?= " \
  /home/sanchayan/beagle/poky/meta \
  /home/sanchayan/beagle/poky/meta-yocto \
  "</code></pre>
<p style='text-align: justify;'>Open a different terminal and go to the **openembedded-core** in **poky** directory.  Make a **conf **directory and add a **layer.conf** file as below.</p>
<pre><code>
# We have a conf and classes directory, append to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have a recipes directory, add to BBFILES
BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend"
BBFILE_COLLECTIONS += "openembedded-core"
BBFILE_PATTERN_openembedded-core := "^${LAYERDIR}/"
BBFILE_PRIORITY_openembedded-core = "4"</code></pre>
<p style='text-align: justify;'>Now go to the **meta-openembedded** directory in **poky**. Make a **conf** directory and add a **layer.conf** file as below.</p>
<pre><code>
# We have a conf and classes directory, append to BBPATH
BBPATH .= ":${LAYERDIR}"
# We have a recipes directory, add to BBFILES
BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-openembedded"
BBFILE_PATTERN_meta-openembedded := "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-openembedded = "5"
</code></pre>
<p style='text-align: justify;'>Go to the terminal in which you got into the **poky-build** directory after running **source/oe-init-build-env**. Add the following to the **conf/local.conf**.</p>
<pre><code>
BB_NUMBER_THREADS = "4"
PARALLEL_MAKE = "-j 4"
INHERIT += "rm_work"
IMAGE_INSTALL_append = " \
            packagegroup-core-x11 \
            libx11 \            
            qtbase \
            qt3d \
            qtconnectivity \
            qtmultimedia \             
            qtserialport \            
            qtwebsockets \
            qtsvg \
            qtx11extras \
              "</code></pre>
<p style='text-align: justify;'>The BB_NUMBER_THREADS and PARALLEL_MAKE in my **local.conf** is as per the fact that i have a quad core machine. Set it as per your machine configuration. Also, set the **MACHINE** variable in the file. I set it to **MACHINE ?= "beagleboard"**. Just add this line below the default. The IMAGE_INSTALL_append will add the packages specified to any image we build and we are going to do a minimal build. You can add the packages you like.</p>
<p style='text-align: justify;'>First look for a specific package you like at the below link. Do select the relevant branch as per your branch selection in the start of the tutorial.</p>
[http://layers.openembedded.org/layerindex/branch/master/recipes/](http://layers.openembedded.org/layerindex/branch/master/recipes/)
<p style='text-align: justify;'>After this, check the layer in which that package recipe resides. Clone the layer in the same way we added the **meta-ti** or **meta-qt5** layers and add them to the **bblayers.conf** file. If the layer has a dependency you need to clone and add the relevant dependency layer too. I wanted to build qt5, so i added the meta-qt5 layer. If you want to build cherokee, you need to add the meta-webserver layer in which the cherokee recipe resides.</p>
<p style='text-align: justify;'>Some packages fail due to a fetch failure. This is because a particular define for a url is not there in Yocto which Openembedded uses.</p>
<p style='text-align: justify;'>Add the following to **meta/classes/mirrors.bbclass** and **meta/conf/bitbake.conf** in the **poky** source tree respectively. Make sure you add it at the right place.</p>
${SAVANNAH_GNU_MIRROR} http://download-mirror.savannah.gnu.org/releases \n \
${SAVANNAH_NONGNU_MIRROR} http://download-mirror.savannah.nongnu.org/releases \n \
SAVANNAH_GNU_MIRROR = "http://download-mirror.savannah.gnu.org/releases"
SAVANNAH_NONGNU_MIRROR = "http://download-mirror.savannah.nongnu.org/releases"
<p style='text-align: justify;'>A patch for the **poky** tree to do the above is below, which you can apply with **git**.</p>
<pre><code>
diff --git a/meta/classes/mirrors.bbclass b/meta/classes/mirrors.bbclass
index 1fd7cd8..1dd6cd6 100644
--- a/meta/classes/mirrors.bbclass
+++ b/meta/classes/mirrors.bbclass
@@ -19,8 +19,10 @@ ${DEBIAN_MIRROR}    ftp://ftp.si.debian.org/debian/pool \n \
 ${DEBIAN_MIRROR}    ftp://ftp.es.debian.org/debian/pool \n \
 ${DEBIAN_MIRROR}    ftp://ftp.se.debian.org/debian/pool \n \
 ${DEBIAN_MIRROR}    ftp://ftp.tr.debian.org/debian/pool \n \
-${GNU_MIRROR}    ftp://mirrors.kernel.org/gnu \n \
+${GNU_MIRROR}        ftp://mirrors.kernel.org/gnu \n \
 ${KERNELORG_MIRROR}    http://www.kernel.org/pub \n \
+${SAVANNAH_GNU_MIRROR} http://download-mirror.savannah.gnu.org/releases \n \
+${SAVANNAH_NONGNU_MIRROR} http://download-mirror.savannah.nongnu.org/releases \n \
 ftp://ftp.gnupg.org/gcrypt/     ftp://ftp.franken.de/pub/crypt/mirror/ftp.gnupg.org/gcrypt/ \n \
 ftp://ftp.gnupg.org/gcrypt/     ftp://ftp.surfnet.nl/pub/security/gnupg/ \n \
 ftp://ftp.gnupg.org/gcrypt/     http://gulus.USherbrooke.ca/pub/appl/GnuPG/ \n \
diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
index b3786a7..29ed3d3 100644
--- a/meta/conf/bitbake.conf
+++ b/meta/conf/bitbake.conf
@@ -568,6 +568,8 @@ KERNELORG_MIRROR = "http://kernel.org/pub"
 SOURCEFORGE_MIRROR = "http://downloads.sourceforge.net"
 XLIBS_MIRROR = "http://xlibs.freedesktop.org/release"
 XORG_MIRROR = "http://xorg.freedesktop.org/releases"
+SAVANNAH_GNU_MIRROR = "http://download-mirror.savannah.gnu.org/releases"
+SAVANNAH_NONGNU_MIRROR = "http://download-mirror.savannah.nongnu.org/releases"
 
 # You can use the mirror of your country to get faster downloads by putting
 #  export DEBIAN_MIRROR = "ftp://ftp.de.debian.org/debian/pool"
diff --git a/meta/recipes-core/images/core-image-minimal.bb b/meta/recipes-core/images/core-image-minimal.bb
index 9716274..13f9127 100644
--- a/meta/recipes-core/images/core-image-minimal.bb
+++ b/meta/recipes-core/images/core-image-minimal.bb
@@ -8,5 +8,5 @@ LICENSE = "MIT"
 
 inherit core-image
 
-IMAGE_ROOTFS_SIZE ?= "8192"
+#IMAGE_ROOTFS_SIZE ?= "8192"
</code></pre>
<p style='text-align: justify;'>Now, you can build an image for your board by doing **bitbake core-image-minimal**. The generated files and images will be in **poky-build/****tmp/deploy/images/beagleboard**.</p>
<p style='text-align: justify;'>Follow this link and transfer the files to the SD card. I don't know why but putting the **uImage** in **boot** directory doesn't work. Put the **uImage** in the **boot** directory which is in the **root** filesystem.</p>
[https://www.yoctoproject.org/downloads/bsps/dora15/beagleboard](https://www.yoctoproject.org/downloads/bsps/dora15/beagleboard)
<p style='text-align: justify;'>Now, plug in the SD card and boot. It boots very quickly. You are supposed to be connected to the debug serial port. For some reason Ethernet and all USB ports don't work. I am trying to figure out why, will update as soon as i do. X also doesn't seem to work on running startx.</p>
<p style='text-align: justify;'>If you would like to setup qtcreator and use qt5, build **meta-toolchain-qt5** and follow the below link. The link is not exactly for qt5, but, can be used for qt5 setup for beagle. No need to follow the relocation related stuff on the link.</p>
[http://developer.toradex.com/how-to/how-to-set-up-qt-creator-to-cross-compile-for-embedded-linux](http://developer.toradex.com/how-to/how-to-set-up-qt-creator-to-cross-compile-for-embedded-linux)

View File

@ -0,0 +1,15 @@
---
author: Sanchayan Maity
title: Boot Process for Linux in Embedded Systems
tags: linux
---
<p style='text-align: justify;'>Some helpful links</p>
[http://blog.techveda.org/arm-linux-booting-process/](http://blog.techveda.org/arm-linux-booting-process/)
[http://sotodayithought.blogspot.in/2013/01/understanding-of-u-boot.html](http://sotodayithought.blogspot.in/2013/01/understanding-of-u-boot.html)
[http://tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/](http://tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/)
<p style='text-align: justify;'>These links are not necessarily up to date, specific or document the latest, but found them helpful nonetheless.</p>

View File

@ -0,0 +1,64 @@
---
author: Sanchayan Maity
title: How is required information passed to device drivers in Linux kernel
tags: device driver
---
<p style='text-align: justify;'>First the basic is how do you provide information like memory addresses from which the driver will do the register read/writes? Now, depending on the kernel version, this can be different. The older kernels and drivers used the platform architecture to specify/get this information. New kernels and drivers rely on device trees for this information.</p>
So let me show how this is done for both.
<pre><code>
static struct resource foo_resource[] = {
[0] = {
.start = foo_BASE_TEG, /* address */
.end = foo_BASE_TEG + 0xff, /* data */
.flags = IORESOURCE_MEM,
},
[1] = {
/* interrupt assigned during initialisation */
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
}
};
static struct foo_platform_data foo_platdata = {
.osc_freq = 24000000
};
static struct platform_device foo_device = {
.name = "foo_platform",
.id = 0,
.num_resources = ARRAY_SIZE(foo_resource),
.resource = foo_resource,
.dev = {
.platform_data = &foo_platdata,
}
};
</code></pre>
<p style='text-align: justify;'>Something like above will be specified in a board file for the hardware. The resource structure specifies the memory address with .start and .end specifier. This in turn is passed in the platform_device structure. When your driver loads at kernel boot up, in init() the platform driver register function matches the name specified in the driver structure and uses the data passed from platform device above. Or if a probe() is directly called by the kernel on boot up, the platform_device pointer passed to the probe call, can be used to retrieve the platform data. The oscillator frequency was specified in the platform data above, but, any such data can be specified and then accessed in the driver. If pdev is the pointer in the probe call, the platform data will be accessible with pdev->dev.platform_data.The pointer to the resource structure can be had with a call to platform_get_resource. Once the resouce structure pointer is available, an ioremap call will return the address to be used from that point onwards, which will be assigned to an iomem variable. Any writes or read you from here on will be based off the memory address you got in the iomem variable above. The readl and writel functions are used for the ARM architecture to read or write to registers. Now you may not note this functions directly in a driver, as a lot many times the drivers are build around functionality provided by a subsystem, but, ultimately in the back end these functions will be used.</p>
<p style='text-align: justify;'>Have a look at the "can" related structures here</p>
[http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra)
<p style='text-align: justify;'>and then have a look at the driver code below, especially the probe() call</p>
[http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/mtd/maps/tegra_nor.c?h=tegra](http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/mtd/maps/tegra_nor.c?h=tegra)
<p style='text-align: justify;'>In recent kernels, device trees are used. For example, an ADC peripheral have a device tree node specification as below</p>
<pre><code>
adc0: adc@4003b000
{
compatible = "fsl,vf610-adc";
reg = <0x4003b000 0x1000>;
interrupts = <0 53 0x04>;
clocks = <&clks VF610_CLK_ADC0>;
clock-names = "adc";
status = "disabled";
#io-channel-cells = <1>;
};
</code></pre>
<p style='text-align: justify;'>So, 0x4003b000 is the starting address of the peripheral. Have a look here [http://lxr.free-electrons.com/source/drivers/iio/adc/vf610_adc.c](http://lxr.free-electrons.com/source/drivers/iio/adc/vf610_adc.c). The of_device_id matches against the name specified in the node and uses the information from the node. Have a look at the driver, probe() function especially and this is pretty simple. Shows how the memory information is read in from the node and used further. Also, one can clearly see the readl() and writel() calls. Do note that however not all drivers will use readl and writely directly, but, have several cascaded pointer calls which will ultimately result in a call to readl or writel. The data in a device tree node can be retrieved with device tree node helper functions.</p>

View File

@ -0,0 +1,164 @@
---
author: Sanchayan Maity
title: .emacs
tags: emacs
---
<p style='text-align: justify;'>I started using Emacs a while back and can't believe how I worked without it in gedit. One can customise Emacs anyway one likes. Here is my .emacs file which enables gtags mode for C files, indentation for C source files, named sessions saving/naming, automatic brace pairings, maximise on opening emacs and changing frames with shift and arrow keys. Do note that nothing in the below .emacs file is my work, it has all been collected from different places on the internet.</p>
<p style='text-align: justify;'>Most helpful has been:</p>
[http://scottfrazersblog.blogspot.in/2009/12/emacs-named-desktop-sessions.html](http://scottfrazersblog.blogspot.in/2009/12/emacs-named-desktop-sessions.html)
<pre><code>(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(ansi-color-names-vector
["#212526" "#ff4b4b" "#b4fa70" "#fce94f" "#729fcf" "#e090d7" "#8cc4ff" "#eeeeec"])
'(custom-enabled-themes (quote (deeper-blue)))
'(inhibit-startup-screen t))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
(when (>= emacs-major-version 24)
(require 'package)
(package-initialize)
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)
)
(add-hook 'c-mode-hook 'ggtags-mode)
(desktop-save-mode 1)
(require 'cl)
(setq-default c-basic-offset 4 c-default-style "linux")
(setq-default tab-width 4 indent-tabs-mode t)
(require 'autopair)
(autopair-global-mode)
(custom-set-variables
'(initial-frame-alist (quote ((fullscreen . maximized)))))
(global-auto-revert-mode t)
(windmove-default-keybindings)
(setq windmove-wrap-around t)
(add-to-list 'load-path "~/.emacs.d/lisp/")
(require 'sr-speedbar)
(defun c-lineup-arglist-tabs-only (ignored)
"Line up argument lists by tabs, not spaces"
(let* ((anchor (c-langelem-pos c-syntactic-element))
(column (c-langelem-2nd-pos c-syntactic-element))
(offset (- (1+ column) anchor))
(steps (floor offset c-basic-offset)))
(* (max steps 1)
c-basic-offset)))
(add-hook 'c-mode-common-hook
(lambda ()
;; Add kernel style
(c-add-style
"linux-tabs-only"
'("linux" (c-offsets-alist
(arglist-cont-nonempty
c-lineup-gcc-asm-reg
c-lineup-arglist-tabs-only))))))
(add-hook 'c-mode-hook
(lambda ()
(let ((filename (buffer-file-name)))
;; Enable kernel mode for the appropriate files
(when (and filename
(string-match (expand-file-name "~/src/linux-trees")
filename))
(setq indent-tabs-mode t)
(c-set-style "linux-tabs-only")))))
(require 'desktop)
(defvar my-desktop-session-dir
(concat (getenv "HOME") "/.emacs.d/desktop-sessions/")
"*Directory to save desktop sessions in")
(defvar my-desktop-session-name-hist nil
"Desktop session name history")
(defun my-desktop-save (&optional name)
"Save desktop by name."
(interactive)
(unless name
(setq name (my-desktop-get-session-name "Save session" t)))
(when name
(make-directory (concat my-desktop-session-dir name) t)
(desktop-save (concat my-desktop-session-dir name) t)))
(defun my-desktop-save-and-clear ()
"Save and clear desktop."
(interactive)
(call-interactively 'my-desktop-save)
(desktop-clear)
(setq desktop-dirname nil))
(defun my-desktop-read (&optional name)
"Read desktop by name."
(interactive)
(unless name
(setq name (my-desktop-get-session-name "Load session")))
(when name
(desktop-clear)
(desktop-read (concat my-desktop-session-dir name))))
(defun my-desktop-change (&optional name)
"Change desktops by name."
(interactive)
(let ((name (my-desktop-get-current-name)))
(when name
(my-desktop-save name))
(call-interactively 'my-desktop-read)))
(defun my-desktop-name ()
"Return the current desktop name."
(interactive)
(let ((name (my-desktop-get-current-name)))
(if name
(message (concat "Desktop name: " name))
(message "No named desktop loaded"))))
(defun my-desktop-get-current-name ()
"Get the current desktop name."
(when desktop-dirname
(let ((dirname (substring desktop-dirname 0 -1)))
(when (string= (file-name-directory dirname) my-desktop-session-dir)
(file-name-nondirectory dirname)))))
(defun my-desktop-get-session-name (prompt &optional use-default)
"Get a session name."
(let* ((default (and use-default (my-desktop-get-current-name)))
(full-prompt (concat prompt (if default
(concat " (default " default "): ")
": "))))
(completing-read full-prompt (and (file-exists-p my-desktop-session-dir)
(directory-files my-desktop-session-dir))
nil nil nil my-desktop-session-name-hist default)))
(defun my-desktop-kill-emacs-hook ()
"Save desktop before killing emacs."
(when (file-exists-p (concat my-desktop-session-dir "last-session"))
(setq desktop-file-modtime
(nth 5 (file-attributes (desktop-full-file-name (concat my-desktop-session-dir "last-session"))))))
(my-desktop-save "last-session"))
(add-hook 'kill-emacs-hook 'my-desktop-kill-emacs-hook)
</code></pre>

View File

@ -0,0 +1,23 @@
---
author: Sanchayan Maity
title: Configuring Mutt and Emacs with AUCTeX
tags: mutt, emacs, auctex
---
<p style='text-align: justify;'>Lately I have been very busy with my MS studies and finding it difficult to find time for anything else. One of the things I have had to do is submit one assignment atleast every week. It is expected that you turn in those assignments in PDF format and no, a shoddily word to PDF converted document doesn't look anywhere professional. I had to pick up LaTeX quickly for the task and it is fucking awesome. I could never get formatting right whenever I had to use MS Word, though yes I never took the time to really learn it either, but, use of command specifiers kinda thingy to get what you want in LaTeX seems easy to me.</p>
<p style='text-align: justify;'>Now, having been using Emacs for the past few months, what better tool to use for churning out documents with LaTeX than Emacs and AUCTeX combo!</p>
[http://mathieu.3maisons.org/wordpress/how-to-configure-emacs-and-auctex-to-work-with-a-pdf-viewer](http://mathieu.3maisons.org/wordpress/how-to-configure-emacs-and-auctex-to-work-with-a-pdf-viewer)
<p style='text-align: justify;'>So, this link helped me get Emacs with AUCTeX running without any problem. In case you are not a fan of Emacs, have a look at Gummi. I did my first two assignments with it and it was damn simple to use. No clutter, simple and you see the preview on the side as you type. And in case you would like to use external packages for LaTeX in gummi, refer the below link.</p>
[http://www.pamelatoman.net/blog/2013/11/using-additional-packages-with-gummi/](http://www.pamelatoman.net/blog/2013/11/using-additional-packages-with-gummi/)
<p style='text-align: justify;'>I have also been using Mutt as my mail client for a while. Not having to use the mouse really makes working with stuff faster, be it Emacs or Mutt. These two links should make setting up Mutt easier for anybody.</p>
[http://pbrisbin.com/posts/mutt_gmail_offlineimap/](http://pbrisbin.com/posts/mutt_gmail_offlineimap/)
[http://pbrisbin.com/posts/two_accounts_in_mutt/](http://pbrisbin.com/posts/two_accounts_in_mutt/)
<p style='text-align: justify;'>Command line for the win!!!!!!! And thanks to both of the guys and Pamela Toman for their informative articles.</p>

View File

@ -0,0 +1,17 @@
---
author: Sanchayan Maity
title: My experience with migration to github pages
tags: github, blog, jekyll
---
<p style='text-align: justify;'>I generally tend to search for a few things randomly and in the process come across good blogs, which are worthwhile visiting regularly. In the last few months, I was coming across pages on the github.io domain and they had such a simple and catchy layout. A very good example is this blog which I stumbled while searching for code related setup in Emacs.</p>
[http://tuhdo.github.io/c-ide.html](http://tuhdo.github.io/c-ide.html)
<p style='text-align: justify;'>I absolutely loved it. Last month I started looking into what exactly was it all about. After going through some blog posts about user experiences of migrating from wordpress to github, I realised it is exactly what I would like to do for my blog.</p>
<p style='text-align: justify;'>Basically github allows you to host your blog on the github.io domain. A particular setup is required and once you set it up, you can basically write your posts in simple markdown (which is much easier to use in my opinion than wordpress) and once you are done, just do a git commit and then git push. And viola your post will be available on the github.io domain. The blog url will be {github_account_name}.github.io. It is pretty cool and you can even check locally how the posts look like with jekyll. This basically allows me to blog using just git and emacs without being bothered with account logins and google authentication for wordpress. Posting code is also so much easier. Absolutely perfect!!!</p>
<p style='text-align: justify;'>I followed the instructions on the below website to get this all up. This is the one which worked for me. A lot of blog posts out there were mentioning importing a plugin in wordpress and then going ahead, but, I really did not understand that. Probably there is some other way to use wordpress than how I was using it.</p>
[http://jlordiales.me/2014/10/30/migrating-to-github-pages/](http://jlordiales.me/2014/10/30/migrating-to-github-pages/)

View File

@ -0,0 +1,42 @@
---
author: Sanchayan Maity
title: Making emacs theme and terminal theme play along nicely
tags: emacs
---
<p style='text-align: justify;'> I have been using emacs for more than six months now and still had a few things which were annoying me as I had not really completely configured it. Today I fixed all the things which were annoying me.</p>
<p style='text-align: justify;'>When editing from the command line, I use to resort to using nano or more recently vim. I was using nano as the text editor for editing my git logs, patches and as an editor for mutt too. Why not use emacs for all of them? Well if you open single emacs instances all the time, it takes time to load the .emacs or init.el file and even if you invoke it as emacs -Q -nw it is not really worthwhile. What is required is to run emacs in daemon mode. I created a systemd service file to start emacs in daemon mode at startup with this script taken from the Arch wiki.</p>
<pre><code>
[Unit]
Description=Emacs: the extensible, self documenting text editor
[Service]
Type=forking
ExecStart=/usr/bin/emacs --daemon
ExecStop=/usr/bin/emacsclient --eval "(kill emacs)"
Restart=always
[Install]
WantedBy=default.target
</pre></code>
<p style='text-align: justify;'>This results in emacs automatically being started in daemon/server mode. Added a few aliases to my .zshrc file as below.</p>
<pre><code>
alias e="emacsclient -t"
alias ec="emacsclient -c"
</pre></code>
<p style='text-align: justify;'>Now I can open emacs from command line as "e filename". And it will open quickly now. So all good? No, still one problem remained. I use solarized theme for my terminal and mutt while I use zenburn for emacs. On opening a file as above, the color themes I guess superimpose or mix and result in a very bad display of text. If I run emacs -Q -nw to open a file it would be alright, as the -Q option does not lead emacs to load it's init.el file. I now needed a way to tell emacsclient to not apply any theme while being opened on a terminal. After some searching, adding the below snippet to init.el resulted in what I wanted. So if I run a GUI emacs, zenburn theme is used, if I use emacs in terminal, no background theme gets applied.</p>
<pre><code>
(defun on-frame-open (frame)
(if (not (display-graphic-p frame))
(set-face-background 'default "unspecified-bg" frame)))
(on-frame-open (selected-frame))
(add-hook 'after-make-frame-functions 'on-frame-open)
</pre></code>
And viola......

View File

@ -0,0 +1,65 @@
---
author: Sanchayan Maity
title: Starting with Linux Kernel development
tags: linux, kernel
---
<p style='text-align: justify;'> I started teaching myself Linux since end of 2012 and since last few months I am finally getting to do what I wanted to. Gonna share here some points which you should keep in mind before starting with Linux.</p>
## C
<p style='text-align: justify;'>One should know C well. If you do not, spend atleast half a year if not more to learn it.</p>
The K & R C book and [this](http://c.learncodethehardway.org/book/) should be where you should probably start.
## Books one should have read
* The Linux Programming Interface - Michael Kerrisk
* Linux Device Drivers - Corbet, Rubini & Hartman
* Linux Kernel Development - Robert Love
<p style='text-align: justify;'>Make sure you have atleast read the above three.</p>
## Tools
<p style='text-align: justify;'>It is important that one knows how to use the right tools. git is the first you should learn. It is an awesome distributed version control system and one can learn it from the following book. It's pretty good and covers everything one needs while being an easy read.</p>
[Git Pro Book](https://git-scm.com/book/en/v2)
<p style='text-align: justify;'>Next comes the editors. If you are coming from an application programming background, you might reach out for an IDE. I would recommend spending some time and learning how to use vim or emacs. Both are extremely powerful and configurable. Do not get into the editor wars, try both and see which one suits you. I picked emacs because the modal nature of vim is somehow not to my liking. Pick one and keep learning it. Do not be discouraged by the fact that it takes time to master them, it will be worthwhile your time. While you will not become a master, but, you will be productive in a few days.</p>
<p style='text-align: justify;'>And last but not the least learn how to use the command line well.</p>
## Email Client
<p style='text-align: justify;'>If you start doing kernel development, you will have to send emails and patches. Patches can be send via git however while replaying to mails on mailing lists, all mails are expected to be in plain text. No HTML or base64 attachments. Gmail web interface won't do. Thunderbird can be configured for this. However, if you do come to like the command line try having a look at Mutt. You can find how to configure it in my previous posts. Also have a look at the kernel documentation.</p>
[Email Client Configuration](http://lxr.free-electrons.com/source/Documentation/email-clients.txt)
## Kernel Documents to be aware of
* [Code Of Conflict](http://lxr.free-electrons.com/source/Documentation/CodeOfConflict)
* [Coding Style](http://lxr.free-electrons.com/source/Documentation/CodingStyle)
* [Submitting Patches](http://lxr.free-electrons.com/source/Documentation/SubmittingPatches)
* [Applying Patches](http://lxr.free-electrons.com/source/Documentation/applying-patches.txt)
* [Kernel Documentation](http://lxr.free-electrons.com/source/Documentation/)
## Read Code
<p style='text-align: justify;'>No substitute for this. The only way one can learn is to read and read more code. At some point, you will start to get it. It will start to all make sense, like Neo sees the Matrix in the end...</p>
## Some Practice
<p style='text-align: justify;'>Get a board like Beagleboard xM or Beaglebone and get started. I started by getting my hands dirty with Beagleboard xM and you need to learn how to use tools like Buildroot and Openembedded, build a root file system, flash kernel image and the root file system.</p>
## Install Linux
<p style='text-align: justify;'>Install Linux. Try with perhaps a virtual machine first but I did recommend going with a full blown dedicated install. Try Gentoo, Slackware or Arch Linux. These distributions give more control to the user and you will get to learn things you probably wouldn't with something like Ubuntu. Arch Linux has a great wiki which is a treasurehouse of information. Install it and start learning command line, building your own kernels and booting them.</p>
## Eudyptula
Check out the Eudyptula Challenge at eudyptula-challenge.org.

View File

@ -0,0 +1,51 @@
---
author: Sanchayan Maity
title: Using OS containers for OpenEmbedded build
tags: openembedded, containers
---
<p style='text-align: justify;'> I use Arch Linux on my laptop and on the server which I use and maintain in office for my openembedded builds. However openembedded does not exactly like rolling release distros it seems. Somehow for whatever reason, my current openembedded builds just fail. I remember facing the problem when I was using Arch and learning openembedded during my early days which lead to my first openembedded post. Why? I do not know nor do I have the time to debug such a thing when there is another solution at hand.</p>
<p style='text-align: justify;'>Now I am certainly not going to stop using Arch Linux, I like it quite a bit with the setup I have. The solution is to use a chroot jail or having systemd as my init, that would be resorting to the use of systemd-nspawn. So lets get on how to set up an environment which we can use for openembedded builds. I assume you have yaourt installed. yaourt is pretty cool as I do not have to bother with downloading pkgbuilds anymore.</p>
<pre><code>
yaourt -S debootstrap
mkdir ~/debian-root
sudo debootstrap --arch=amd64 --variant=minbase jessie ~/debian-root
sudo systemd-nspawn -D debian-root
</code></pre>
<p style='text-align: justify;'>The third command is gonna take a while. After you run the last command you would be inside the chroot jail as a root user. After that run the following.</p>
<pre><code>
apt-get install update
apt-get install sudo
adduser -uid 1000 -gid 100 username
visudo
su username
</code></pre>
<p style='text-align: justify;'>I assume you know what to add after running visudo. Basically add yourself to the sudoers file. In case you are wondering how I got the user id and group id for the adduser command, that would be by running "id" on my host. The last command will drop you in the home directory for the newly created user. Whenever you want to work on openembedded, just use systemd-nspawn to log in to the debian environment. You can keep this environment for as long you like to maintain your build setup while your normal userspace can be updated without fear of breaking the build. You can replicate the exact debian environment by just copying the complete debian root directory using something like rsync to any number of your machines.</p>
<p style='text-align: justify;'>Nice ain't it? Now I don't have to bother that something will break when I update. I am writing this while my openembedded build is going on. I mean to look into how vagrant, docker and CoreOS are exactly used. May be it will be something which can be used to help our customers replicate the setup which we know to be working at our end. Do not know if it is actually possible, but I read somewhere something like this atleast in the context of vagrant while messing around a bit with vim related stuff. Also do note that debian is not the only one you can use. You can do this for Fedora, Arch and probably other distros too, however I have not tried that. This is my first time using this. I do remember using a chroot jail while setting up Arch Linux, but, it's not something I looked closely into which I mean to rectify soon.</p>
<p style='text-align: justify;'>Output from my debian root environment.</p>
<pre><code>
sanchayan@debian-root:~/oe-core/build$ uname -a
Linux debian-root 4.0.0-ck1-bfs-ck+ #8 SMP PREEMPT Wed Apr 29 11:35:34 IST 2015 x86_64 GNU/Linux
sanchayan@debian-root:~/oe-core/build$ pwd
/home/sanchayan/oe-core/build
</code></pre>
<p style='text-align: justify;'>Output of machinectl from my host.</p>
<pre><code>
machinectl
MACHINE CLASS SERVICE
debian-root container nspawn
1 machines listed.
</code></pre>
[systemd for Administrators](http://0pointer.net/blog/archives.html) series should be interesting to you.
<p style='text-align: justify;'>Do have a look at the man pages for systemd-nspawn and machinectl.</p>

View File

@ -0,0 +1,178 @@
---
author: Sanchayan Maity
title: Implementing multithreaded epoll
tags: epoll
---
<p style='text-align: justify;'>Finally I have some time to relax a bit and write about a few things now that my fall semester course ended. I had taken a Distributed Systems class where we were taught the basics of distributed systems, had some assignments, with the final assignment being evaluation of some of the distributed key-value stores. The most interesting thing I learned on my own was how to use epoll in a multithreaded design. Was actually trying to use the libevent library during my second assignment, however, due to my lack of understanding I could not get it to work the way I wanted. During my third assignment I tried using epoll directly, but, I wanted to do a multithreaded design. Searching on the internet did not get me any examples, only notes on how one might implement it. So I tried and finally got it working. This will probably be the first write up on the net to be talking about this. However I am no expert in such things, so if anyone has any suggestions or improvements to what I did, please do share in the comments or mail me directly.</p>
<p style='text-align: justify;'>One can clone the concerned repository with</p>
<pre><code>
git clone https://github.com/SanchayanMaity/Multithreaded_Epoll.git
</code></pre>
<p style='text-align: justify;'>The first requirement is a workqueue implementation. During my work with libevent and looking for usable examples, I came across one which was being used with libevent by the code's author Ron Cemer [1]. The second requirement was to have a good usable epoll example I could use which I found here [2].</p>
<pre><code>
[1]. http://roncemer.com/software-development/multi-threaded-libevent-server-example/
[2]. https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/
</code></pre>
<p style='text-align: justify;'>With the above I had a workqueue implementation and a usable epoll example, on which I could base my work upon. So let us jump to the thread which uses epoll and acts as the main event loop queueing up work for the workqueues to process.</p>
<pre><code>
efd = epoll_create1(0);
if (efd == -1)
err_sys("Error with epoll_create1");
event.data.fd = listenfd;
event.events = EPOLLIN;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &event);
if (ret == -1)
err_sys("Error with epoll_ctl");
/* Buffer where events are returned */
events = calloc(MAXEVENTS, sizeof(event));
/* The event loop */
for ( ; ; ) {
client_length = addr_length;
n = epoll_wait(efd, events, MAXEVENTS, -1);
for (i = 0; i < n; i++) {
......
}
</code></pre>
<p style='text-align: justify;'>The listen descriptor is created when the main loop starts. The listen descriptor is added to the list of descriptors epoll should wait on. After that we start our infinite loop, in which we block with epoll waiting for events on this listen descriptor. Once any event occurs on the listen descriptor, which in this case are the incoming connection requests, we iterate over the event list. There are three possibilites from here on. First is of course some error occured, so we check for errors, we also check if connections were closed with the EPOLLHUP and if it is not the event we registered for.</p>
<pre><code>
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN))) {
/*
* An error has occured on this fd or the socket is not ready
* for reading. Why were we notified then?
*/
printf("epoll error\n");
close(events[i].data.fd);
for (int j = 0; j < MAX_NO_OF_SERVERS; j++)
if (events[i].data.fd == sconn[i].sockfd) {
sconn[i].sockfd = -1;
sconn[i].server_connected = false;
}
continue;
}
</code></pre>
<p style='text-align: justify;'>Second is an incoming connection request. In this case we accept the incoming connection, and then add the descriptor returned by accept to the descriptor list of events epoll should let us know about. This descriptor would be the one on which incoming and outgoing messages will be processed and send respectively.</p>
<pre><code>
if (listenfd == events[i].data.fd) {
/*
* We have a notification on the listening socket, which means
* one or more incoming connections.
*/
while (1) {
infd = accept(listenfd, client_address, &client_length);
if (infd == -1) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
/* We processed all incoming connections */
break;
}
else {
perror("Error with accept");
break;
}
} else
printf("Connection accepted\n");
/*
* Make the incoming socket non blocking and add it to
* the lists of fds to monitor. In the future when the
* read/write calls are made non blocking this will be
* required.
*/
/*
if (!make_socket_nonblocking(infd)) {
perror("Could not make socket nonblocking");
abort();
}
*/
event.data.fd = infd;
event.events = EPOLLIN | EPOLLERR | EPOLLHUP;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
if (ret == -1) {
perror("Error with epoll_ctl");
abort();
}
}
continue;
}
</code></pre>
<p style='text-align: justify;'>Third case is an incoming request on one of the connections that was accepted in second step. In this case, we dynamically allocate a job and add it to the workqueue along with the respective data required. However before adding the job to the workqueue, we make sure to remove the descriptor which we will process, from the list of event descriptors epoll will monitor. See the comment in code.</p>
<pre><code>
else {
if ((job = malloc(sizeof(* job))) == NULL) {
perror("Failed to allocate memory for job object");
continue;
}
if ((job_wq_data = malloc(sizeof(* job_wq_data))) == NULL) {
perror("Failed to allocate memory for wq data");
free(job);
continue;
}
job_wq_data->sockfd = events[i].data.fd;
job->job_function = process_peer_request;
job->user_data = job_wq_data;
/*
* In a multi threaded environment epoll is not suppose to monitor
* descriptors on which other threads are working. Ideally use of
* the EPOLLONESHOT flag should have disabled it for the next epoll_wait
* till the worker thread reenables it, however for some reason it seems
* not to work. So manually delete the fd from being monitored by epoll
* and add it back in the process_peer_request function after it finishes
* working with the said descriptor. In future use EPOLLONESHOT after
* investigation and finding the fix.
*/
ret = epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, &events[i]);
if (ret == -1)
perror("Error with epoll_ctl DEL");
/* Add the job for processing by workqueue */
workqueue_add_job(&workqueue, job);
}
</code></pre>
<p style='text-align: justify;'>The above code snippets are from the dht.c file which is the primary file. The workqueue implementation can be seen and understood by going through the workqueue.c and workqueue.h files. Note that the function called by the workqueue will basically do the job of processing the incoming client request as per one's protocol requirements. Workqueue initialisation is simple enough as below</p>
<pre><code>
/* Initialize work queue */
if (workqueue_init(&workqueue, NUMBER_OF_WQS)) {
workqueue_shutdown(&workqueue);
perror("Failed to create workqueue");
goto free_tokens;
}
</code></pre>
<p style='text-align: justify;'>At the end of the request processing function, the descriptor which was passed to the function for processing needs to be added back to the list of descriptors epoll should monitor.</p>
<pre><code>
event.data.fd = connfd;
event.events = EPOLLIN;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);
if (ret == -1)
perror("Error with epoll_ctl ADD");
free(job_wq_data);
free(job);
</code></pre>
<p style='text-align: justify;'>The server thread with the epoll event loop will only be one and depending on the number of cores one would initialise the number of workqueues. So since I had a four core system, I had three workqueues and one thread with epoll event loop. This kind of a system can scale well instead of naive approaches like one thread or one process per client connection. Further work to be done are to get the EPOLLONESHOT flag to work as I am sure I was definitely doing something wrong while trying to use it and second would be using non blocking sockets instead of blocking one, however that will be quite a bit of work. Non blocking sockets are not so easy to handle.</p>

111
site.hs Normal file
View File

@ -0,0 +1,111 @@
--------------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid (mappend)
import Hakyll
--------------------------------------------------------------------------------
main :: IO ()
main = hakyll $ do
match "images/*" $ do
route idRoute
compile copyFileCompiler
match "css/*" $ do
route idRoute
compile compressCssCompiler
match (fromList ["contact.markdown"]) $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= relativizeUrls
match (fromList ["about.markdown"]) $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= relativizeUrls
tags <- buildTags "posts/*" (fromCapture "tags/*.html")
tagsRules tags $ \tag pattern -> do
let title = "Posts tagged \"" ++ tag ++ "\""
route idRoute
compile $ do
posts <- recentFirst =<< loadAll pattern
let ctx = constField "title" title
`mappend` listField "posts" postCtx (return posts)
`mappend` defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/tag.html" ctx
>>= loadAndApplyTemplate "templates/default.html" ctx
>>= relativizeUrls
match "posts/*" $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/post.html" (postCtxWithTags tags)
>>= loadAndApplyTemplate "templates/default.html" (postCtxWithTags tags)
>>= relativizeUrls
create ["archive.html"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let archiveCtx =
listField "posts" postCtx (return posts) `mappend`
constField "title" "Archives" `mappend`
defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
>>= loadAndApplyTemplate "templates/default.html" archiveCtx
>>= relativizeUrls
match "index.html" $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let indexCtx =
listField "posts" postCtx (return posts) `mappend`
constField "title" "Home" `mappend`
defaultContext
getResourceBody
>>= applyAsTemplate indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= relativizeUrls
create ["sitemap.xml"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let sitemapCtx =
constField "baseUrl" "sanchayanmaity.github.io" `mappend`
constField "title" "Sitemap" `mappend`
listField "posts" postCtx (return posts) `mappend`
postCtx
makeItem ""
>>= loadAndApplyTemplate "templates/sitemap.xml" sitemapCtx
>>= cleanIndexHtmls
match "templates/*" $ compile templateBodyCompiler
--------------------------------------------------------------------------------
postCtx :: Context String
postCtx =
dateField "date" "%B %e, %Y" `mappend`
defaultContext
postCtxWithTags :: Tags -> Context String
postCtxWithTags tags = tagsField "tags" tags `mappend` postCtx
cleanIndexHtmls :: Item String -> Compiler (Item String)
cleanIndexHtmls = return . fmap (replaceAll pattern replacement)
where
pattern = "/index.html"
replacement :: String -> String
replacement = const "/"

66
stack.yaml Normal file
View File

@ -0,0 +1,66 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# http://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: nightly-2017-02-09
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# extra-dep: true
# subdirs:
# - auto-update
# - wai
#
# A package marked 'extra-dep: true' will only be built if demanded by a
# non-dependency (i.e. a user package), and its test suites and benchmarks
# will not be run. This is useful for tweaking upstream packages.
packages:
- '.'
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
extra-deps: []
# Override default flag values for local packages and extra-deps
flags: {}
# Extra package databases containing global packages
extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.3"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

2
templates/archive.html Normal file
View File

@ -0,0 +1,2 @@
Here you can find all my previous posts:
$partial("templates/post-list.html")$

33
templates/default.html Normal file
View File

@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Hakyll Blog - $title$</title>
<link rel="stylesheet" href="/css/default.css" />
</head>
<body>
<div id="header">
<div id="logo">
<a href="/">Coherent musings</a>
</div>
<div id="navigation">
<a href="/">Home</a>
<a href="/about.html">About</a>
<a href="/contact.html">Contact</a>
<a href="/archive.html">Archive</a>
</div>
</div>
<div id="content">
<h1>$title$</h1>
$body$
</div>
<div id="footer">
Site proudly generated by
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
</div>
</body>
</html>

7
templates/post-list.html Normal file
View File

@ -0,0 +1,7 @@
<ul>
$for(posts)$
<li>
<a href="$url$">$title$</a> - $date$
</li>
$endfor$
</ul>

13
templates/post.html Normal file
View File

@ -0,0 +1,13 @@
<div class="info">
Posted on $date$
$if(author)$
by $author$
$endif$
</div>
<div class="info">
$if(tags)$
Tags: $tags$
$endif$
</div>
$body$

11
templates/sitemap.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
$for(posts)$
<url>
<loc>http://sanchayanmaity.github.io$url$</loc>
<changefreq>weekly</changefreq>
$if(dateMap)$<lastmod>$dateMap$</lastmod>$endif$
<priority>0.8</priority>
</url>
$endfor$
</urlset>

1
templates/tag.html Normal file
View File

@ -0,0 +1 @@
$partial("templates/post-list.html")$