Sunday, April 01, 2012

USB pendrive installer from DVD installer

Again, it has been a long time since my last post. :-) This weekend I was trying to help my mom with her Linux Mint 12 installation. So, I burnt an installer DVD for her, only to find out that her old laptop's DVD drive is not working any more. It just can't read the DVD. Without downloading yet another USB pendrive image, I had to find a way to convert the installer DVD into a pendrive installer. So, here is what I did:
1. Mount the DVD installer on my own laptop, which DVD drive is working and read the size (block count) using the df command:
[weichong@aspire4810tz ~]$ df Filesystem 1K-blocks Used Available Use% Mounted on /dev/sr0 1052128 1052128 0 100% /media/Linux Mint 12 32-bit
2. Use dd to extract the ISO from the DVD drive:
[weichong@aspire4810tz ~]$ dd if=/dev/sr0 of=./linuxmint12.iso bs=1024 count=1052128
3. Use md5sum to verify that the ISO image is the same with the original ISO image in the distro website:
[weichong@aspire4810tz ~]$ md5sum ./Downloads/linuxmint12.iso ee3d6e2ca498bc7685b7f17cdb5f2eea ./Downloads/linuxmint12.iso
4. Plug a USB pendrive into my laptop (/dev/sdb1), and follow these steps here to produce a bootable pendrive:
[weichong@aspire4810tz ~]$ su -c 'livecd-iso-to-disk ./Downloads/linuxmin12.iso /dev/sdb1'
5. Copy all the content from the mounted DVD drive to the pendrive (/media/LIVE):
[weichong@aspire4810tz ~]$ cp -R /media/Linux\ Mint\ 12\ 32-bit/* /media/LIVE/
6. Plug in this USB pendrive to my mom's laptop and boot from there. Note that the steps described here an account I try to recall. Hopefully, I didn't recall anything wrongly by mistake. If I do, hehe, this is April fool's joke. :-)

Thursday, July 14, 2011

Patch to build OpenCV 2.3.0 with ffmpeg

It has been ages I have not update my blog at all. Partly due to busy schedule, and partly due to laziness :P
Lately I am trying out OpenCV on my Ubuntu 10.04 LTS box.
Due to some issue in the old pre built libcv-dev with v4l2 which cause the webcam not able to work properly without PRELOAD=/usr/lib/libv4l/v4l1compat.so, I tried to build my own OpenCV 2.3.0 and ffmpeg from source.
However, I realize that OpenCV 2.3.0 still have not keep up with the latest changes in ffmpeg. So, I either have to content with using the ffmpeg that comes with the OpenCV source, or I have to patch it to work. I chose that later as I wanted it to use my custom ffmpeg.
Thinking that it might benefits other I try to post it here, just in case:
Index: OpenCV-2.3.0/modules/highgui/src/cap_ffmpeg_impl.hpp
===================================================================
--- OpenCV-2.3.0.orig/modules/highgui/src/cap_ffmpeg_impl.hpp 2011-07-10 11:26:54.000000000 +0800
+++ OpenCV-2.3.0/modules/highgui/src/cap_ffmpeg_impl.hpp 2011-07-10 11:50:15.000000000 +0800
@@ -135,7 +135,13 @@
#define PIX_FMT_RGBA32 PIX_FMT_RGB32
#endif

+#ifndef CODEC_TYPE_VIDEO
+#define CODEC_TYPE_VIDEO AVMEDIA_TYPE_VIDEO
+#endif

+#ifndef PKT_FLAG_KEY
+#define PKT_FLAG_KEY AV_PKT_FLAG_KEY
+#endif

char * FOURCC2str( int fourcc )
{
@@ -550,7 +556,7 @@

// First time we're called, set packet.data to NULL to indicate it
// doesn't have to be freed
- if (bFirstTime) {
+ if (bFirstTime) {
bFirstTime = false;
packet.data = NULL;
}
@@ -561,7 +567,7 @@
// free last packet if exist
if (packet.data != NULL) {
av_free_packet (&packet);
- }
+ }

// get the next frame
while (!valid) {
@@ -574,17 +580,20 @@
if( packet.stream_index != video_stream ) {
av_free_packet (&packet);
continue;
- }
+ }

-#if LIBAVFORMAT_BUILD > 4628
- avcodec_decode_video(video_st->codec,
- picture, &got_picture,
- packet.data, packet.size);
-#else
- avcodec_decode_video(&video_st->codec,
- picture, &got_picture,
- packet.data, packet.size);
-#endif
+//#if LIBAVFORMAT_BUILD > 4628
+// avcodec_decode_video(video_st->codec,
+// picture, &got_picture,
+// packet.data, packet.size);
+//#else
+// avcodec_decode_video(&video_st->codec,
+// picture, &got_picture,
+// packet.data, packet.size);
+//#endif
+avcodec_decode_video2(video_st->codec,
+ picture, &got_picture,
+ &packet);

if (got_picture) {
// we have a new picture, so memorize it
@@ -825,16 +834,16 @@
static const char * icvFFMPEGErrStr(int err)
{
switch(err) {
- case AVERROR_NUMEXPECTED:
- return "Incorrect filename syntax";
+// case AVERROR_NUMEXPECTED:
+// return "Incorrect filename syntax";
case AVERROR_INVALIDDATA:
return "Invalid data in header";
- case AVERROR_NOFMT:
- return "Unknown format";
- case AVERROR_IO:
- return "I/O error occurred";
- case AVERROR_NOMEM:
- return "Memory allocation error";
+// case AVERROR_NOFMT:
+// return "Unknown format";
+// case AVERROR_IO:
+// return "I/O error occurred";
+// case AVERROR_NOMEM:
+// return "Memory allocation error";
default:
break;
}
@@ -1237,7 +1246,7 @@
av_register_all ();

/* auto detect the output format from the name and fourcc code. */
- fmt = guess_format(NULL, filename, NULL);
+ fmt = av_guess_format(NULL, filename, NULL);
if (!fmt)
return false;

@@ -1260,7 +1269,7 @@
#endif

// alloc memory for context
- oc = av_alloc_format_context();
+ oc = avformat_alloc_context();
assert (oc);

/* set file name */

By the way, for those who intend to build OpenCV 2.2.0 instead, patches already exist here.

Hope it helps. Enjoy! :)

Friday, May 28, 2010

tty anyone? reading serially...

Text terminal (and of course, console, pseudo-terminal, virtual console and terminal emulator) or TTY (teletype printer) as it is usually called, due to historical reason, is something any average Linux/UNIX user would use on a daily basis, in one form or another.
Its internal mechanism is indeed something that has always catch my attention and curiosity. Since today is a holiday, I took some time off to trace the code behind the serial interface (like ttyS0). Below is what I learn...(or so I think).

Before diving deep into the rest of the source, lets look at some definition.
#include/linux/serial_reg.h:
#define UART_RX 0 /* In: Receive buffer */
#define UART_LSR 5 /* In: Line Status Register */
#define UART_LSR_BI 0x10 /* Break interrupt indicator */
#define UART_LSR_DR 0x01 /* Receiver data ready */

And also the central structure tty_struct, which due to history reason, also include data for the line discipline.
#include/linux/tty.h:
struct tty_struct {
...
struct tty_ldisc *ldisc;
...
struct tty_bufhead buf;
...
wait_queue_head_t read_wait;
...
* The following is data for the N_TTY line discipline. For
* historical reasons, this is included in the tty structure.
...
char *read_buf;
...
struct tty_port *port;
};

When there are data on the serial (UART) interface, and the interrupt is trigger, the interrupt service routine serial8250_interrupt() is invoked.
drivers/serial/8250.c:
static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
...
if (!(iir & UART_IIR_NO_INT)) {
serial8250_handle_port(up);
...
}

static void serial8250_handle_port(struct uart_8250_port *up)
{
...
if (status & (UART_LSR_DR | UART_LSR_BI))
receive_chars(up, &status);
...
}

static void
receive_chars(struct uart_8250_port *up, unsigned int *status)
{
...
do {
if (likely(lsr & UART_LSR_DR))
ch = serial_inp(up, UART_RX);
...
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);

ignore_char:
lsr = serial_inp(up, UART_LSR);
} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
...
tty_flip_buffer_push(tty);
...
}
#include/linux/serial_core.h:
static inline void
uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
...
tty_insert_flip_char(tty, ch, flag);
...
}

From the interrupt service routine, the callee will eventually copy the data to the corresponding tty_buffer.
#include/linux/tty_flip.h:
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
...
struct tty_buffer *tb = tty->buf.tail;
...
tb->char_buf_ptr[tb->used++] = ch;
...
}

Then, the data is push/flush to the corresponding line discipline.
drivers/char/tty_buffer.c:
* Queue a push of the terminal flip buffers to the line discipline. This
void tty_flip_buffer_push(struct tty_struct *tty)
{
...
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schedule_delayed_work(&tty->buf.work, 1);
}

static void flush_to_ldisc(struct work_struct *work)
{
...
struct tty_ldisc *disc;
...
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);
...
}
#include/linux/tty_ldisc.h:
struct tty_ldisc_ops {
...
/*
* The following routines are called from below.
*/
void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
char *fp, int count);
void (*write_wakeup)(struct tty_struct *);

...
};

struct tty_ldisc {
struct tty_ldisc_ops *ops;
...
};

The line discipline's receive_buf hook will copy the data to its own read buffer and wake up the process that is waiting (sleeping) for the incoming data.
drivers/char/n_tty.c:
* Called by the terminal driver when a block of characters has
* been received. This function must be called from soft contexts
* not from interrupt context. The driver is responsible for making
* calls one at a time and in order (or using flush_to_ldisc)
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
...
memcpy(tty->read_buf + tty->read_head, cp, i);
...
if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
}
...
}

struct tty_ldisc_ops tty_ldisc_N_TTY = {
...
.receive_buf = n_tty_receive_buf,
...
};

And that is how the program (like the bash shell that is running on ttyS0) receive the data!
Or at least that is what I think how it all works.
If time permits, it would be great to continue digging into how write operation works on ttyS0, and other "terminals" like pty etc.