2010-12-21
2010-12-16
2010-11-11
Linux: The I2C Subsystem
Source: http://www.linuxjournal.com/article/7252 (Part II)
/*
* Tiny I2C Chip Driver
*
* Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This driver shows how to create a minimal I2C bus and algorithm driver.
*
* Compile this driver with:
echo "obj-m := tiny_i2c_chip.o" > Makefile
make -C <path/to/kernel/src> SUBDIRS=$PWD modules
*/
#define DEBUG 1
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x00, 0xff, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(chip);
/* Each client has this additional data */
struct chip_data {
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 temp_input; /* Register values */
u16 temp_max;
u16 temp_hyst;
};
#define REG_TEMP_HYST 0x01
#define REG_TEMP_OS 0x02
static int chip_attach_adapter(struct i2c_adapter *adapter);
static int chip_detect(struct i2c_adapter *adapter, int address, int kind);
static int chip_detach_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver chip_driver = {
.owner = THIS_MODULE,
.name = "tiny_chip",
.flags = I2C_DF_NOTIFY,
.attach_adapter = chip_attach_adapter,
.detach_client = chip_detach_client,
};
static void chip_update_client(struct i2c_client *client)
{
struct chip_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++data->temp_input;
++data->temp_max;
++data->temp_hyst;
data->last_updated = jiffies;
data->valid = 1;
up(&data->update_lock);
}
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct chip_data *data = i2c_get_clientdata(client); \
\
chip_update_client(client); \
return sprintf(buf, "%d\n", data->value); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
#define set(value, reg) \
static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct chip_data *data = i2c_get_clientdata(client); \
int temp = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = temp; \
up(&data->update_lock); \
return count; \
}
set(temp_max, REG_TEMP_OS);
set(temp_hyst, REG_TEMP_HYST);
static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
static int chip_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, chip_detect);
}
/* This function is called by i2c_detect */
static int chip_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client = NULL;
struct chip_data *data = NULL;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto error;
new_client = kmalloc(sizeof(*new_client), GFP_KERNEL);
if (!new_client) {
err = -ENOMEM;
goto error;
}
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto error;
}
memset(new_client, 0x00, sizeof(*new_client));
memset(data, 0x00, sizeof(*data));
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &chip_driver;
new_client->flags = 0;
/* Fill in the remaining client fields */
strncpy(new_client->name, "tiny_chip", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
err = i2c_attach_client(new_client);
if (err)
goto error;
/* Register sysfs files */
device_create_file(&new_client->dev, &dev_attr_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp_input);
return 0;
error:
kfree(new_client);
kfree(data);
return err;
}
static int chip_detach_client(struct i2c_client *client)
{
struct chip_data *data = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
return err;
}
kfree(client);
kfree(data);
return 0;
}
static int __init tiny_init(void)
{
return i2c_add_driver(&chip_driver);
}
static void __exit tiny_exit(void)
{
i2c_del_driver(&chip_driver);
}
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
MODULE_DESCRIPTION("Tiny i2c chip");
MODULE_LICENSE("GPL");
module_init(tiny_init);
module_exit(tiny_exit);
/*
* Tiny I2C Chip Driver
*
* Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This driver shows how to create a minimal I2C bus and algorithm driver.
*
* Compile this driver with:
echo "obj-m := tiny_i2c_chip.o" > Makefile
make -C <path/to/kernel/src> SUBDIRS=$PWD modules
*/
#define DEBUG 1
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x00, 0xff, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(chip);
/* Each client has this additional data */
struct chip_data {
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 temp_input; /* Register values */
u16 temp_max;
u16 temp_hyst;
};
#define REG_TEMP_HYST 0x01
#define REG_TEMP_OS 0x02
static int chip_attach_adapter(struct i2c_adapter *adapter);
static int chip_detect(struct i2c_adapter *adapter, int address, int kind);
static int chip_detach_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver chip_driver = {
.owner = THIS_MODULE,
.name = "tiny_chip",
.flags = I2C_DF_NOTIFY,
.attach_adapter = chip_attach_adapter,
.detach_client = chip_detach_client,
};
static void chip_update_client(struct i2c_client *client)
{
struct chip_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++data->temp_input;
++data->temp_max;
++data->temp_hyst;
data->last_updated = jiffies;
data->valid = 1;
up(&data->update_lock);
}
#define show(value) \
static ssize_t show_##value(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct chip_data *data = i2c_get_clientdata(client); \
\
chip_update_client(client); \
return sprintf(buf, "%d\n", data->value); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
#define set(value, reg) \
static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct chip_data *data = i2c_get_clientdata(client); \
int temp = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = temp; \
up(&data->update_lock); \
return count; \
}
set(temp_max, REG_TEMP_OS);
set(temp_hyst, REG_TEMP_HYST);
static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
static int chip_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, chip_detect);
}
/* This function is called by i2c_detect */
static int chip_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client = NULL;
struct chip_data *data = NULL;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto error;
new_client = kmalloc(sizeof(*new_client), GFP_KERNEL);
if (!new_client) {
err = -ENOMEM;
goto error;
}
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto error;
}
memset(new_client, 0x00, sizeof(*new_client));
memset(data, 0x00, sizeof(*data));
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &chip_driver;
new_client->flags = 0;
/* Fill in the remaining client fields */
strncpy(new_client->name, "tiny_chip", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
err = i2c_attach_client(new_client);
if (err)
goto error;
/* Register sysfs files */
device_create_file(&new_client->dev, &dev_attr_temp_max);
device_create_file(&new_client->dev, &dev_attr_temp_min);
device_create_file(&new_client->dev, &dev_attr_temp_input);
return 0;
error:
kfree(new_client);
kfree(data);
return err;
}
static int chip_detach_client(struct i2c_client *client)
{
struct chip_data *data = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
return err;
}
kfree(client);
kfree(data);
return 0;
}
static int __init tiny_init(void)
{
return i2c_add_driver(&chip_driver);
}
static void __exit tiny_exit(void)
{
i2c_del_driver(&chip_driver);
}
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
MODULE_DESCRIPTION("Tiny i2c chip");
MODULE_LICENSE("GPL");
module_init(tiny_init);
module_exit(tiny_exit);
2010-11-10
Linux: The I2C Subsystem
Source: http://www.linuxjournal.com/article/7136 (Part I)
/*
* Tiny I2C Adapter Driver
*
* Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This driver shows how to create a minimal I2C bus and algorithm driver.
*
* Compile this driver with:
echo "obj-m := tiny_i2c_adap.o" > Makefile
make -C <path/to/kernel/src> SUBDIRS=$PWD modules
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/i2c.h>
#include <linux/init.h>
static s32 tiny_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
int i;
int len;
dev_info(&adap->dev, "%s was called with the following parameters:\n",
__FUNCTION__);
dev_info(&adap->dev, "addr = %.4x\n", addr);
dev_info(&adap->dev, "flags = %.4x\n", flags);
dev_info(&adap->dev, "read_write = %s\n",
read_write == I2C_SMBUS_WRITE ? "write" : "read");
dev_info(&adap->dev, "command = %d\n", command);
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_info(&adap->dev, "size = I2C_SMBUS_PROC_CALL\n");
break;
case I2C_SMBUS_QUICK:
dev_info(&adap->dev, "size = I2C_SMBUS_QUICK\n");
break;
case I2C_SMBUS_BYTE:
dev_info(&adap->dev, "size = I2C_SMBUS_BYTE\n");
break;
case I2C_SMBUS_BYTE_DATA:
dev_info(&adap->dev, "size = I2C_SMBUS_BYTE_DATA\n");
if (read_write == I2C_SMBUS_WRITE)
dev_info(&adap->dev, "data = %.2x\n", data->byte);
break;
case I2C_SMBUS_WORD_DATA:
dev_info(&adap->dev, "size = I2C_SMBUS_WORD_DATA\n");
if (read_write == I2C_SMBUS_WRITE)
dev_info(&adap->dev, "data = %.4x\n", data->word);
break;
case I2C_SMBUS_BLOCK_DATA:
dev_info(&adap->dev, "size = I2C_SMBUS_BLOCK_DATA\n");
if (read_write == I2C_SMBUS_WRITE) {
dev_info(&adap->dev, "data = %.4x\n", data->word);
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
for (i = 1; i <= len; i++)
dev_info(&adap->dev, "data->block[%d] = %.2x\n",
i, data->block[i]);
}
break;
}
return 0;
}
static u32 tiny_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static struct i2c_algorithm tiny_algorithm = {
.name = "tiny algorithm",
.smbus_xfer = tiny_access,
};
static struct i2c_adapter tiny_adapter = {
.owner = THIS_MODULE,
.class = I2C_ADAP_CLASS_SMBUS,
.algo = &tiny_algorithm,
.name = "tiny adapter",
};
static int __init tiny_init(void)
{
return i2c_add_adapter(&tiny_adapter);
}
static void __exit tiny_exit(void)
{
i2c_del_adapter(&tiny_adapter);
}
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
MODULE_DESCRIPTION("Tiny i2c adapter");
MODULE_LICENSE("GPL");
module_init(tiny_init);
module_exit(tiny_exit);
* Tiny I2C Adapter Driver
*
* Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This driver shows how to create a minimal I2C bus and algorithm driver.
*
* Compile this driver with:
echo "obj-m := tiny_i2c_adap.o" > Makefile
make -C <path/to/kernel/src> SUBDIRS=$PWD modules
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/i2c.h>
#include <linux/init.h>
static s32 tiny_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
int i;
int len;
dev_info(&adap->dev, "%s was called with the following parameters:\n",
__FUNCTION__);
dev_info(&adap->dev, "addr = %.4x\n", addr);
dev_info(&adap->dev, "flags = %.4x\n", flags);
dev_info(&adap->dev, "read_write = %s\n",
read_write == I2C_SMBUS_WRITE ? "write" : "read");
dev_info(&adap->dev, "command = %d\n", command);
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_info(&adap->dev, "size = I2C_SMBUS_PROC_CALL\n");
break;
case I2C_SMBUS_QUICK:
dev_info(&adap->dev, "size = I2C_SMBUS_QUICK\n");
break;
case I2C_SMBUS_BYTE:
dev_info(&adap->dev, "size = I2C_SMBUS_BYTE\n");
break;
case I2C_SMBUS_BYTE_DATA:
dev_info(&adap->dev, "size = I2C_SMBUS_BYTE_DATA\n");
if (read_write == I2C_SMBUS_WRITE)
dev_info(&adap->dev, "data = %.2x\n", data->byte);
break;
case I2C_SMBUS_WORD_DATA:
dev_info(&adap->dev, "size = I2C_SMBUS_WORD_DATA\n");
if (read_write == I2C_SMBUS_WRITE)
dev_info(&adap->dev, "data = %.4x\n", data->word);
break;
case I2C_SMBUS_BLOCK_DATA:
dev_info(&adap->dev, "size = I2C_SMBUS_BLOCK_DATA\n");
if (read_write == I2C_SMBUS_WRITE) {
dev_info(&adap->dev, "data = %.4x\n", data->word);
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
for (i = 1; i <= len; i++)
dev_info(&adap->dev, "data->block[%d] = %.2x\n",
i, data->block[i]);
}
break;
}
return 0;
}
static u32 tiny_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static struct i2c_algorithm tiny_algorithm = {
.name = "tiny algorithm",
/*.id = I2C_ALGO_SMBUS,
id: Description of the type of algorithm this structure defines. These different types are defined in the include/linux/i2c-id.h file and start with the characters I2C_ALGO_.
*/
.smbus_xfer = tiny_access,
/*
A function pointer called by the I2C core to determine what kind of reads and writes the I2C adapter driver can do.
All of the different I2C message types are defined in include/linux/i2c.h and start with the characters I2C_FUNC_.
*/.functionality = tiny_func,
};
static struct i2c_adapter tiny_adapter = {
.owner = THIS_MODULE,
.class = I2C_ADAP_CLASS_SMBUS,
.algo = &tiny_algorithm,
.name = "tiny adapter",
};
static int __init tiny_init(void)
{
return i2c_add_adapter(&tiny_adapter);
}
static void __exit tiny_exit(void)
{
i2c_del_adapter(&tiny_adapter);
}
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
MODULE_DESCRIPTION("Tiny i2c adapter");
MODULE_LICENSE("GPL");
module_init(tiny_init);
module_exit(tiny_exit);
2010-10-12
Linux: Useful programs for embedded Linux systems
- Dropbear: SSH server and client let you get a remote console on the target device and Copy files to/from the target device.
- thttpd: Tiny/Turbo/Throttling HTTP server
- Boa Webserver
- lighthttpd Webserver
- TinyLogin
To be continued...
C Programming: Bit Masking
Bit Masking Table:
1 - > Bit 1Example:
2 - > Bit 2
4 - > Bit 3
8 - > Bit 4
16 - > Bit 5
32 - > Bit 6
64 - > Bit 7
128 - > Bit 8
if(buffer[0] & 1 == 0){// Frist bit is set to 0}
else{// First bit is set to 1}
if(buffer[0] & 64 == 0){// Bit number 7 is set to 0}else{// Bit number 7 is set to 1}
Linux: Quick note, U-Boot and BusyBox Compiling
# Root File System
mkfs.jffs2 -r root-fs/ -o myRootImage.jffs2 -e 128 -l -n
# U-Boot
$make distclean
$export PATH=/opt/arm-2007q1/bin/:$PATH
$make at91sam9263ek_config
$make CROSS_COMPILE=/opt/arm-2007q1/bin/arm-none-linux-gnueabi-
$mkimage -A arm -O linux -C none -T kernel -a 20008000 -e 20008000 -n linux-2.6 -d arch/arm/boot/Image uImage
$mkimage -A arm -O linux -C none -T kernel -a 20008000 -e 20008000 -n linux-2.6 -d arch/arm/boot/zImage uImage
# Busybox
$make defconfig
$make ARCH=arm CROSS_COMPILE=/opt/arm-2007q1/bin/arm-none-linux-gnueabi- CONFIG_STATIC=y CONFIG_PREFIX=/home/mohamed/busyboxfs install
mkfs.jffs2 -r root-fs/ -o myRootImage.jffs2 -e 128 -l -n
# U-Boot
$make distclean
$export PATH=/opt/arm-2007q1/bin/:$PATH
$make at91sam9263ek_config
$make CROSS_COMPILE=/opt/arm-2007q1/bin/arm-none-linux-gnueabi-
$mkimage -A arm -O linux -C none -T kernel -a 20008000 -e 20008000 -n linux-2.6 -d arch/arm/boot/Image uImage
$mkimage -A arm -O linux -C none -T kernel -a 20008000 -e 20008000 -n linux-2.6 -d arch/arm/boot/zImage uImage
# Busybox
$make defconfig
$make ARCH=arm CROSS_COMPILE=/opt/arm-2007q1/bin/arm-none-linux-gnueabi- CONFIG_STATIC=y CONFIG_PREFIX=/home/mohamed/busyboxfs install
Embedded: SPI Bitbanging
#define CLKLOW() // Put SCLK GPIO to LOW
#define CLKHIGH() // Put SCLK GPIO to HIGH
#define ASSERTCS() // Put /CS GPIO to LOW
#define DEASSERTCS() // Put /CS GPIO to HIGH
#define READMISO() // MISO GPIO status?
#define MOSIHIGH() // MOSI GPIO to HIGH
#define MOSILOW() // MOSI GPIO to LOW
#define POWERON() // VCC GPIO to HIGH
#define POWERDOWN // VCC GPIO to LOW
#define SPISPPED 140
void spi_init()
{
POWERON();
CLKHIGH();
DEASSERTCS();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void spi_write_byte_SPIBitBang8BitsMode0(unsigned char byte)
{
unsigned char bit;
ASSERTCS();
for (bit = 0; bit < 8; bit++) {
/* write MOSI on trailing edge of previous clock */
if (byte & 0x80)
MOSIHIGH();
else
MOSILOW();
byte <<= 1;
/* half a clock cycle before leading/rising edge */
SPIDELAY(SPISPEED/2);
CLKHIGH();
/* half a clock cycle before trailing/falling edge */
SPIDELAY(SPISPEED/2);
/* read MISO on trailing edge */
//byte |= READMISO();
CLKLOW();
}
DEASSERTCS();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void spi_write_byte(unsigned char byte) /* Pseudo Code */{TEMP = 100000000B;SS = 0;SCK = 1;
WHILE(TEMP)MOSI = (WRITE_BUFFER && TEMP);TEMP >> 1;SCK ^= 1;END WHILESS = 1}
/////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char spi_read_byte() /* Pseudo Code */{SS = 0;SCK = 1;Count = 8;WHILE(count)READ_BUFFER | = MISO;READ_BUFFER << 1;Count--;SCK ^= 1;END WHILESS = 1}
////////////////////////
void spi_stop(){POWERDOWN();}
For more information on SPI, please visit this Wikipedia article
Linux: Mounting JFFS2 on an MTD RAM Device
$ modprobe jffs2
$ modprobe mtdblock
$ modprobe mtdram
$ dd if=jffs2.bin of=/dev/mtdblock0
$ mount -t jffs2 /dev/mtdblock0 /mnt/flash
And now to save changes we do:
$ dd if=/dev/mtdblock0 of=new-jffs2.bin.
Linux: Creating a JFFS2 Root File System
mkfs.jffs2 -r /mnt/flash -e 128 -b -o rootfs.jffs2
First we mount the ext2 file system image on a loopback device on an arbitrary mount point on our development workstation. Next we invoke the MTD utility mkfs.jffs2 to create the JFFS2 file system image.
The -r flag tells mkfs.jffs2 where the root file system image is located.
The -e instructs mkfs.jffs2 to build the image while assuming a 128KB block size. The default is 64KB. JFFS2 does not exhibit its most efficient behavior if the Flash device contains a different block size than the block size of the image. Finally, we display a long listing and discover that the resulting JFFS2 root file system image has been reduced in size by more than 60 percent. When you are working with limited Flash memory, this is a substantial reduction in precious Flash resource usage.
The -b flag is the -big-endian flag. This instructs the mkfs.jffs2 utility to create a JFFS2 Flash image suitable for use on a big-endian target.
Finally we boot target using:
console=ttyS0,115200 rootfstype=jffs2 root=/dev/mtdblock2
Linux: Adding a new boot argument to the Kernel
static unsigned int my_super_new_boot_argument = 1;
is_my_super_new_boot_argumennt(char *str)
{
get_option(&str, &my_super_new_boot_argument);
return 1;
}
/* Handle parameter "my_super_new_boot_argument=" */
__setup("my_super_new_boot_argument=", is_my_super_new_boot_argumennt);
if (my_super_new_boot_argument)
{
/* ... */
}
2010-09-21
Linux: Creating Linux Kernel U-Boot Images
To make a U-Boot image file containing the Elf binary vmlinux kernel file, it is first required to use objcopy to get the raw binary data, followed by the mkimage tool to encapsulate it (as a .ub file). In this example the kernel is also compressed to save space (this is recommended, but not necessary).
The addresses for the two parameters -a (load address) and -e (execute address) depend of the specific target board that is being used.
And if we want also to make an INITRD image we do -T ramdisk
$ arm-linux-objcopy -O binary vmlinux arm-vmlinux.bin
$ gzip --best --force arm-vmlinux.bin
$ mkimage -A arm -O linux -T kernel -C gzip -a 0x8C001000 -e 0x8C002000\ -n "ARM Linux 2.6" -d arm-vmlinux.bin.gz arm-vmlinux.ub
The addresses for the two parameters -a (load address) and -e (execute address) depend of the specific target board that is being used.
And if we want also to make an INITRD image we do -T ramdisk
2010-09-13
Linux: Managing double linked list in the Linux kernel
#include <linux/list.h>
- LIST_HEAD(list_name): Creat an new double linked list.
- list_add(n,p): Inserts an element pointed to by n right after the specified element pointed to by p. To insert n at the beginning of the list, set p to the address of the list head.
- list_add_tail(n,p): Inserts an element pointed to by n right before the specified element pointed to by p. To insert n at the end of the list, set p to the address of the list head.
- list_del(p): Deletes an element pointed to by p.
- list_empty(p): Checks if the list specified by the address p of its head is empty.
- list_entry(p,t,m): Returns the address of the data structure of type t in which the list_head field that has the name m and the address p is included.
- list_for_each(p,h): Scans the elements of the list specified by the address h of the head; in each iteration, a pointer to the list_head structure of the list element is returned in p.
- list_for_each_entry(p,h,m): Returns the address of the data structure embedding the list_head structure rather than the address of the list_head structure itself.
2010-09-09
Linux: Catchnig Net Device Notification in the Linux Kernel
A Net Device notification, is sent when we access a network device. To catch such event we do:
Name will be the name of the network device (eth0, eth1, etc...).
#include <linux/notifier.h>
#include <asm/kdebug.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
static struct notifier_block my_dev_notifier =
{
.notifier_call = my_dev_event_handler,
};
int my_dev_event_handler(struct notifier_block *self, unsigned long val, void *data)
{
printk("my_dev_event: Val=%ld, Interface=%s\n", val, ((struct net_device *) data)->name);
return 0;
}
/* ... */
register_netdevice_notifier(&my_dev_notifier);In this example, Val will take 1 (when we do from the shell ifconfig eth0 up) or 0 (ifconfig eth0 down).
Name will be the name of the network device (eth0, eth1, etc...).
2010-09-08
Linux: Catching Die Notification in the Linux Kernel
A die notification (die_chain), is sent when a kernel function triggers a trap or a fault, caused by an oops, page fault, or a breakpoint hit. To catch such event we do:
#include <linux/notifier.h>
#include <asm/kdebug.h>
static struct notifier_block my_die_notifier =
{
.notifier_call = my_die_event_handler,
};
int my_die_event_handler(struct notifier_block *self, unsigned long val, void *data)
{
struct die_args *args = (struct die_args *) data;
if (val == 1) /* oops */
{
printk("my_die_event: OOPs! at EIP=%lx\n", args->regs->eip);
}
return 0;
}
/* ... */
register_die_notifier(&my_die_notifier);
Linux: Making some delays at the kernel context
To make some delays in the kernel context there are two solutions.
First, busy-waiting (for two seconds):
First, busy-waiting (for two seconds):
unsigned long timeout = jiffies + (2*HZ);Or we can use sleep-waiting (better):
while (time_before(jiffies, timeout)) continue;
unsigned long timeout = jiffies + (2*HZ);Also, we have the msleep() function for sleeping in milliseconds, udelay() for microseconds delays and ndelay() for nanoseconds delays. We should include the header file <asm/delay.h>
schedule_timeout(timeout);
Linux: Measuring Boot Time
// Purpose of this utility is to timestamp each line coming over stdin // USAGES: // tstamp.exe < /dev/ttyS0 // <commands> | tstamp.exe // #include <stdio.h> #include <time.h> #include <sys/time.h> char buf[10*1024]; main(int argc) { struct timeval tv; double time_start,time_now,time_prev; int first=1; if(argc!=1) // as of now no arguments are allowed. print usage { printf("Timestamps each line coming over stdin\n" "\tUSAGES:\n" "\t\ttstamp.exe < /dev/ttyS0\n" "\t\t<commands> | tstamp.exe\n" "\t\tetc..\n"); printf("Output is printed in the following format\n" "\tcolumn1 is elapsed time since first message" "\tcolumn2 is elapsed time since previous message\n" "\tcolumn3 is the message\n"); exit(0); } printf("\tcolumn1 is elapsed time since first message\n" "\tcolumn2 is elapsed time since previous message\n" "\tcolumn3 is the message\n"); while(1) { if(gets(buf)) { gettimeofday(&tv, NULL); // get system time time_now = tv.tv_sec + (tv.tv_usec*(1.0/1000000.0)); // convert to double if(first) // if first time, notedown the time_start first = 0, time_start = time_prev = time_now; printf("%03.3f %02.3f: ",(float)(time_now-time_start), (float)(time_now-time_prev)); // print the column1 and 2 puts(buf); // now print the message as column3 time_prev = time_now; } } }
2010-09-07
Linux: Mount Root File System on NFS
To enable your embedded system to mount its root file system via NFS we must configure our target's kernel for NFS support. There is also a configuration option to enable root mounting of an NFS remote directory.
To statically configure our target's IP address, our kernel command line might look like this:
Here, client-ip is the target's IP address; server-ip is the address of the NFS server; gw-ip is the gateway (router), in case the server-ip is on a different subnet; and netmask defines the class of IP addressing. hostname is a string that is passed as the target hostname; device is the Linux device name, such as eth0; and PROTO defines the protocol used to obtain initial IP parameters.
For a sample example using DHCP protocol:
To statically configure our target's IP address, our kernel command line might look like this:
console=ttyS0,115200 ip=192.168.1.139:192.168.1.1:192.168.1.1:255.255.255.0:embedded_host:eth0:off nfsroot=192.168.1.1:/home/me/my_remote_folder root=/dev/nfs rwWhere:
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<PROTO>
Here, client-ip is the target's IP address; server-ip is the address of the NFS server; gw-ip is the gateway (router), in case the server-ip is on a different subnet; and netmask defines the class of IP addressing. hostname is a string that is passed as the target hostname; device is the Linux device name, such as eth0; and PROTO defines the protocol used to obtain initial IP parameters.
For a sample example using DHCP protocol:
console=ttyS0,115200 root=/dev/nfs rw ip=dhcp
nfsroot=192.168.1.9://home/me/my_remote_folder
Linux: Mounting NFS Filesystem
First we should export the directory that will be mounted by the remote computer via NFS, we do:
On an embedded system with NFS enabled, the following command mounts the my_remote_folder directory exported by the NFS server on a mount point of our choosing:
echo "/home/me/my_remote_folder *(rw,sync,no_root_squash)" > /etc/exportsWe should then restart our NFS server with the new configuration file using:
/etc/init.d/nfs restart
or
- /usr/sbin/exportfs -av
- /sbin/service nfs restart
On an embedded system with NFS enabled, the following command mounts the my_remote_folder directory exported by the NFS server on a mount point of our choosing:
mount -t nfs mycomputer:/home/me/my_remote_folder /mnt/nfsWhere mycomputer is the name of your computer on the network or his IP address.
2010-09-01
U-Boot: Booting Linux Kernel with Initrd support
# tftpboot 0x10000000 kernel-uImage
...
Load address: 0x10000000
Loading: ############################ done
Bytes transferred = 1069092 (105024 hex)
# tftpboot 0x10800000 initrd-uboot
...
Load address: 0x10800000
Loading: ########################################### done
Bytes transferred = 282575 (44fcf hex)
# bootm 0x10000000 0x10800040
Uncompressing kernel.................done.
...
RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize
...
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 filesystem).
Greetings: this is linuxrc from Initial RAMDisk
Mounting /proc filesystem
BusyBox v1.00 (2005.03.14-16:37+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
# (<<<< Busybox command prompt)
...
Load address: 0x10000000
Loading: ############################ done
Bytes transferred = 1069092 (105024 hex)
# tftpboot 0x10800000 initrd-uboot
...
Load address: 0x10800000
Loading: ########################################### done
Bytes transferred = 282575 (44fcf hex)
# bootm 0x10000000 0x10800040
Uncompressing kernel.................done.
...
RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize
...
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 filesystem).
Greetings: this is linuxrc from Initial RAMDisk
Mounting /proc filesystem
BusyBox v1.00 (2005.03.14-16:37+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
# (<<<< Busybox command prompt)
2010-08-27
Linux: Using Tasklet/Workqueues in Bottom Half Interrupt
In this post we will see how to use Tasklet and Workqueue to deal with interrupts in the Linux Kernel.
First we declare our Tasklet using DECLARE_TASKLET(name, function, data):
Now to schedule our Tasklet we do:
If we want to use Workqueue:
First we declare our Tasklet using DECLARE_TASKLET(name, function, data):
void do_tasklet(unsigned long);As you see name is the name of our Tasklet, function is the function that will called to treat the tasklet, data is what we bill passed to this function (unsigned long).
DECLARE_TASKLET(my_tasklet, do_tasklet, 0);
Now to schedule our Tasklet we do:
tasklet_schedule(&my_tasklet);
If we want to use Workqueue:
#include <linux/workqueue.h>
static struct work_struct my_wq;
INIT_WORK(&my_wq, (void (*)(void *)) do_tasklet, NULL);
...
schedule_work(&my_wq);
2010-08-26
Linux: Interrupts in the Linux kernel
In this post, we will see how to deal with interrupts when writing device drivers for Linux.
First we should include the header file <linux/interrupt.h>
To deal with interrupts we use two functions:
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int irq, void *dev_id),
unsigned long flags,
const char *dev_name,
void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
The first function will return 0 if it succeed, or a negative value for fail.
int init_module()
{
/*
* Since the keyboard handler won't co-exist with another handler,
* such as us, we have to disable it (free its IRQ) before we do
* anything. Since we don't know where it is, there's no way to
* reinstate it later - so the computer will have to be rebooted
* when we're done.
*/
free_irq(1, NULL);
/*
* Request IRQ 1, the keyboard IRQ, to go to our irq_handler.
* SA_SHIRQ means we're willing to have othe handlers on this IRQ.
* SA_INTERRUPT can be used to make the handler into a fast interrupt.
*/
return request_irq(1, /* The number of the keyboard IRQ on PCs */
irq_handler, /* our handler */
SA_SHIRQ,
"keyboard_irq_handler",
(void *)(irq_handler));
}
void cleanup_module()
{
/*
* This is only here for completeness. It's totally irrelevant, since
* we don't have a way to restore the normal keyboard interrupt so the
* computer is completely useless and has to be rebooted.
*/
free_irq(1, NULL);
}
*((char *)scancode) & 0x80 ? "Released" : "Pressed");
return IRQ_HANDLED;
}
Now to enable/disable interrupts, we use respectively enable_irq(unsigned int irq_number) and disable_irq(unisigned int irq_number).
First we should include the header file <linux/interrupt.h>
To deal with interrupts we use two functions:
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int irq, void *dev_id),
unsigned long flags,
const char *dev_name,
void *dev_id);
void free_irq(unsigned int irq, void *dev_id);
The first function will return 0 if it succeed, or a negative value for fail.
- irq: it's the IRQ number
- handler: is the function that the kernel will call to deal with the requested IRQ
- flags: SA_INTERRUPT for fast interrupt handler, SA_SHIRQ for shared interrupt handler, IRQF_DISABLED flag specifies that this interrupt handler has to be treated as a fast handler, so the kernel has to disable interrupts while invoking the handler, IRQF_TRIGGER_RISING announces a rising-edge interrupt, IRQF_TRIGGER_HIGH for level-sensitive interrupt
- dev_name: this string will be visible form /proc/interrupts
- dev_id: if the interrupt is not shared, it can be set to NULL
int init_module()
{
/*
* Since the keyboard handler won't co-exist with another handler,
* such as us, we have to disable it (free its IRQ) before we do
* anything. Since we don't know where it is, there's no way to
* reinstate it later - so the computer will have to be rebooted
* when we're done.
*/
free_irq(1, NULL);
/*
* Request IRQ 1, the keyboard IRQ, to go to our irq_handler.
* SA_SHIRQ means we're willing to have othe handlers on this IRQ.
* SA_INTERRUPT can be used to make the handler into a fast interrupt.
*/
return request_irq(1, /* The number of the keyboard IRQ on PCs */
irq_handler, /* our handler */
SA_SHIRQ,
"keyboard_irq_handler",
(void *)(irq_handler));
}
void cleanup_module()
{
/*
* This is only here for completeness. It's totally irrelevant, since
* we don't have a way to restore the normal keyboard interrupt so the
* computer is completely useless and has to be rebooted.
*/
free_irq(1, NULL);
}
/*
* This function services keyboard interrupts. It reads the relevant
* information from the keyboard.
*/
irqreturn_t irq_handler(int irq, void *dev_id)
{
unsigned char scancode;
unsigned char status;
/*
* Read keyboard status
*/
status = inb(0x64);
scancode = inb(0x60);
printk(KERN_INFO "Scan Code %x %s.\n",
(int)*((char *)scancode) & 0x7F,* This function services keyboard interrupts. It reads the relevant
* information from the keyboard.
*/
irqreturn_t irq_handler(int irq, void *dev_id)
{
unsigned char scancode;
unsigned char status;
/*
* Read keyboard status
*/
status = inb(0x64);
scancode = inb(0x60);
printk(KERN_INFO "Scan Code %x %s.\n",
*((char *)scancode) & 0x80 ? "Released" : "Pressed");
return IRQ_HANDLED;
}
Now to enable/disable interrupts, we use respectively enable_irq(unsigned int irq_number) and disable_irq(unisigned int irq_number).
2010-08-25
Linux: Sleeping in the Linux kernel
In this post, I will show you how to do sleeping inside your LKM (Linux Kernel Module) by exploring the Wait Queue API.
To use this API in our code, we should include the header file <linux/wait.h>.
First we declare and init our wait queue by calling a dedicated macro in our code:
If we use wait_event, our process is put into an uninterruptible sleep. The preferred alternative is wait_event_interruptible, which can be interrupted by signals. This version returns an integer value that we should check; a nonzero value means our sleep was inter-rupted by some sort of signal, and our driver should probably return -ERESTARTSYS.
The final versions (wait_event_timeout and wait_event_interruptible_timeout) wait for a limited time; after that time period (expressed in jiffies) expires, the macros return with a value of 0 regardless of how condition evaluates.
Now to wake up all process in a given queue we should call:
We now know enough to look at a simple example of sleeping and waking up. In the sample source, you can find a module called sleepy. It implements a device with simple behavior: any process that attempts to read from the device is put to sleep. Whenever a process writes to the device, all sleeping processes are awakened. This behavior is implemented with the following read and write methods:
To use this API in our code, we should include the header file <linux/wait.h>.
First we declare and init our wait queue by calling a dedicated macro in our code:
DECLARE_WAIT_QUEUE_HEAD(name);This is called a static initialization, we can also do a dynamic initialization by using the following code:
wait_queue_head_t my_queue;Now for sleeping our code, we use one of these functions:
init_waitqueue_head(&my_queue);
wait_event(queue, condition)So while condition is false, the process continue to sleep.
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
If we use wait_event, our process is put into an uninterruptible sleep. The preferred alternative is wait_event_interruptible, which can be interrupted by signals. This version returns an integer value that we should check; a nonzero value means our sleep was inter-rupted by some sort of signal, and our driver should probably return -ERESTARTSYS.
The final versions (wait_event_timeout and wait_event_interruptible_timeout) wait for a limited time; after that time period (expressed in jiffies) expires, the macros return with a value of 0 regardless of how condition evaluates.
Now to wake up all process in a given queue we should call:
void wake_up(wait_queue_head_t *queue);The last function restricts itself to processes performing an interruptible sleep.
void wake_up_interruptible(wait_queue_head_t *queue);
We now know enough to look at a simple example of sleeping and waking up. In the sample source, you can find a module called sleepy. It implements a device with simple behavior: any process that attempts to read from the device is put to sleep. Whenever a process writes to the device, all sleeping processes are awakened. This behavior is implemented with the following read and write methods:
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);
wait_event_interruptible(wq, flag != 0);
flag = 0;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
flag = 1;
wake_up_interruptible(&wq);
return count; /* succeed, to avoid retrial */
}
Source: Linux Device Drivers, 3rd Edition
2010-08-24
Linux: Complete BusyBox tutorial
For a complete tutorial how to download, configure, make, and install BusyBox for Linux, please visit this Web page (IBM):
http://www.ibm.com/developerworks/library/l-busybox/
http://www.ibm.com/developerworks/library/l-busybox/
Linux: Extracting a .cpio archive
To extract a .cpio archive we do
cpio -idv < /tmp/initramfs.cpio
Linux: List the contenent of a .cpio archive
Now suppose that we want to verify the contenent of our /tmp/initramfs.cpio (See last post), so we do:
- cpio -idv < /tmp/initramfs.cpio
Linux: Create .cpio archive for initramfs
We suppose that our initramfs directory is under /tmp/initramfs
To create the .cpio archive that will be loaded by the bootloader, we do:
To create the .cpio archive that will be loaded by the bootloader, we do:
- cd /tmp/initramfs
- find . | cpio --quiet -c -o > /tmp/initramfs.cpio
- cat /tmp/initramfs.cpio | gzip > /tmp/initramfs.cpio.gz
- An voilĂ , /tmp/initramfs.cpio.gz can now be passed to the bootloader to be your root file system
2010-08-18
Linux: Calling user-land program from kernel-land in Linux
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
MODULE_LICENSE( "GPL" );
static int __init my_module_init( void )
{
return call_user_land_program();
}
static void __exit my_module_exit( void )
{
return;
}
module_init( my_module_init );
module_exit( my_moduel_exit );
static int call_user_land_program( void )
{
struct subprocess_info *sub_info;
char *argv[] = { "/usr/bin/myprogram", "--help", NULL };
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
sub_info = call_usermodehelper_setup( argv[0], argv, envp, GFP_ATOMIC );
if (sub_info == NULL) return -ENOMEM;
return call_usermodehelper_exec( sub_info, UMH_WAIT_PROC ); // UMH_NO_WAIT,
UMH_WAIT_EXEC
}
Second Solutions (Preferred solution):
static int call_user_land_program( void ) { char *argv[] = { "/usr/bin/myprogram", "--help", NULL };
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );
}
Subscribe to:
Posts (Atom)