Linux2.6.32 for Utu2440(7)

TurnToJPG -->


Watchdog

Change the configuraiton of Watchdog in kernel:
/images/2440watchdog.jpg

Watchdog configuraiton in kernel:

$ grep "CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME" ./ -r
./drivers/watchdog/s3c2410_wdt.c:#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME   (15)

Test Watchdog:

~ # dmesg | grep watchdog
s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled
~ # 
~ # echo 0>/dev/watchdog

s3c2410-wdt s3c2410-wdt: Unexpected close, not stopping watchdog

Then after 15 seconds your board will reset, disable watchdog via:

~ # echo -n V>/dev/watchdog

LED

The connection for LED is:
/images/2440leds.jpg

Saw s3c2440 chip connection:
/images/2440leds1.jpg

Connection in text:

EINT4 GPF4 DL1
EINT5 GPF5 DL2
EINT6 GPF6 DL3
EINT7 GPF7 DL4 

Code changes:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define DEVICE_NAME "leds" //设备名(/dev/leds)
//LED 对应的GPIO 端口列表
static unsigned long led_table [] = {
 S3C2410_GPF(4),
 S3C2410_GPF(5),
 S3C2410_GPF(6),
 S3C2410_GPF(7),
};
//LED 对应端口将要输出的状态列表
static unsigned int led_cfg_table [] = {
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
};
/*ioctl 函数的实现
* 在应用/用户层将通过ioctl 函数向内核传递参数,以控制LED 的输出状态
*/
static int leds_ioctl(struct inode *inode,
          struct file *file,
          unsigned int cmd,
          unsigned long arg)
{
 switch(cmd) {
  case 0:
  case 1:
    if (arg > 4) {
     return -EINVAL;
    }
  //根据应用/用户层传递来的参数(取反),通过s3c2410_gpio_setpin 函数设置LED 对应的端口寄存器
  s3c2410_gpio_setpin(led_table[arg], !cmd);
   return 0;
  default:
    return -EINVAL;
 }
}
/*
* 设备函数操作集,在此只有ioctl 函数,通常还有read, write, open, close 等,因为本LED 驱动在下面已经
* 注册为misc 设备,因此也可以不用open/close
*/
static struct file_operations dev_fops = {
 .owner = THIS_MODULE,
 .ioctl = leds_ioctl,
};
/*
* 把LED 驱动注册为MISC 设备
*/
static struct miscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR, //动态设备号
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};
/*
* 设备初始化
*/
static int __init dev_init(void)
{
 int ret;
 int i;
 for (i = 0; i < 4; i++) {
  //设置LED 对应的端口寄存器为输出(OUTPUT)
  s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
  //设置LED 对应的端口寄存器为低电平输出,在模块加载结束后,四个LED 应该是全部都是发光状态
  s3c2410_gpio_setpin(led_table[i], 0);
 }
 ret = misc_register(&misc); //注册设备

 if(ret < 0)
 {
        printk(DEVICE_NAME "register falid!\n");
        return ret;
  }

 printk (DEVICE_NAME "\tinitialized\n"); //打印初始化信息
 return 0;
}
static void __exit dev_exit(void)
{
 misc_deregister(&misc);
}
//模块初始化,仅当使用insmod/podprobe 命令加载时有用,如果设备不是通过模块方式加载,此处将不会被调用
module_init(dev_init); 
//卸载模块,当该设备通过模块方式加载后,可以通过rmmod 命令卸载,将调用此函数
module_exit(dev_exit); 
MODULE_LICENSE("GPL"); //版权信息
MODULE_AUTHOR("singleboy."); //开发者信息

Change Kconfig file:

config LEDS_SMDK2440
 tristate "LED Support for SMDK2440 GPIO LEDs"
 depends on SMDK2440_CPU2440
 default y if SMDK2440_CPU2440
 help
  This option enables support for LEDs connected to GPIO lines
  on SMDK2440 boards.

And the Makefile

obj-$(CONFIG_LEDS_SMDK2440)     += smdk2440_leds.o

Rebuild the kernel and verify.

Test file:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
 int on;
 int led_no;
 int fd;
 if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||
     on < 0 || on > 1 || led_no < 0 || led_no > 3) {
  fprintf(stderr, "Usage: leds led_no 0|1\n");
  exit(1);
 }
 fd = open("/dev/leds0", 0);
 if (fd < 0) {
  fd = open("/dev/leds", 0);
 }
 if (fd < 0) {
  perror("open device leds");
  exit(1);
 }
 ioctl(fd, on, led_no);
 close(fd);
 return 0;
}

Then run test like:

/root # ./led 0 0
/root # ./led 1 0
/root # ./led 2 0
/root # ./led 2 1
/root # ./led 2 0
/root # ./led 3 0

Write a simple script

while true
 do
./led 0 0
sleep 1
./led 0 1
sleep 1
./led 1 0
sleep 1
./led 1 1
sleep 1
./led 2 0
sleep 1
./led 2 1
sleep 1
./led 3 0
sleep 1
./led 3 1
done

Now you could see led blinks.

Keys

First view the schematic to find the circle connection:
/images/2440buttons.jpg

Keys connection:

K1	EINT0		GPF0
K2	EINT1		GPF1 
K3	EINT2		GPF2
K4	EINT3		GPF3	
K5	EINT11		GPG3
K6	EINT19		GPG11

The definition should be adjusted to the keys connection definition.

Edit the drivers/misc/smdk2440_buttons.c:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#define DEVICE_NAME "buttons" //设备名称
/*定义中断所用的结构体*/
struct button_irq_desc {
 int irq; //按键对应的中断号
 int pin; //按键所对应的GPIO 端口
 int pin_setting; //按键对应的引脚描述,实际并未用到,保留
 int number; //定义键值,以传递给应用层/用户态
 char *name; //每个按键的名称
};
/*结构体实体定义*/
static struct button_irq_desc button_irqs [] = {
 {IRQ_EINT0, S3C2410_GPF(0), S3C2410_GPF0_EINT0, 0, "KEY0"},
 {IRQ_EINT1, S3C2410_GPF(1), S3C2410_GPF1_EINT1, 1, "KEY1"},
 {IRQ_EINT2, S3C2410_GPF(2), S3C2410_GPF2_EINT2, 2, "KEY2"},
 {IRQ_EINT3, S3C2410_GPF(3), S3C2410_GPF3_EINT3, 3, "KEY3"},
 {IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 4, "KEY4"},
 {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"},
};
/*开发板上按键的状态变量,注意这里是’0’,对应的ASCII 码为30*/
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
/*因为本驱动是基于中断方式的,在此创建一个等待队列,以配合中断函数使用;当有按键按下并读取到键
值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用户态;当没有按
键按下时,系统并不会轮询按键状态,以节省时钟资源*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为1,read 函数会把它清零*/
static volatile int ev_press = 0;
/*本按键驱动的中断服务程序*/
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
 struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
 int down;
 // udelay(0);
 /*获取被按下的按键状态*/
 down = !s3c2410_gpio_getpin(button_irqs->pin);
 /*状态改变,按键被按下,从这句可以看出,当按键没有被按下的时候,寄存器的值为1(上拉),但按
  键被按下的时候,寄存器对应的值为0*/
 if (down != (key_values[button_irqs->number] & 1)) { // Changed
  /*如果key1 被按下,则key_value[0]就变为’1’,对应的ASCII 码为31*/
  key_values[button_irqs->number] = '0' + down;
  ev_press = 1; /*设置中断标志为1*/
  wake_up_interruptible(&button_waitq); /*唤醒等待队列*/
 }
 return IRQ_RETVAL(IRQ_HANDLED);
}
/*
*在应用程序执行open(“/dev/buttons”,…)时会调用到此函数,在这里,它的作用主要是注册6 个按键的中断。
*所用的中断类型是IRQ_TYPE_EDGE_BOTH,也就是双沿触发,在上升沿和下降沿均会产生中断,这样做
是为了更加有效地判断按键状态
*/
static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
 int i;
 int err = 0;
 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
  if (button_irqs[i].irq < 0) {
   continue;
  }
  /*注册中断函数*/
  err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
        button_irqs[i].name, (void *)&button_irqs[i]);
  if (err)
   break;
 }
 if (err) { /*如果出错,释放已经注册的中断,并返回*/
  i--;
  for (; i >= 0; i--) {
   if (button_irqs[i].irq < 0) {
    continue;
   }
   disable_irq(button_irqs[i].irq);
   free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
  }
  return -EBUSY;
 }
 /*注册成功,则中断队列标记为1,表示可以通过read 读取*/
 ev_press = 1;
 /*正常返回*/
 return 0;
}
/*
*此函数对应应用程序的系统调用close(fd)函数,在此,它的主要作用是当关闭设备时释放6 个按键的中断*
处理函数
*/
static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
 int i;
 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
  if (button_irqs[i].irq < 0) {
   continue;
  }
  /*释放中断号,并注销中断处理函数*/
  free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
 }
 return 0;
}
/*
*对应应用程序的read(fd,…)函数,主要用来向用户空间传递键值
*/
static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
 unsigned long err;
 if (!ev_press) {
  if (filp->f_flags & O_NONBLOCK)
   /*当中断标识为0 时,并且该设备是以非阻塞方式打开时,返回*/
   return -EAGAIN;
  else
   /*当中断标识为0 时,并且该设备是以阻塞方式打开时,进入休眠状态,等待被唤醒*/
   wait_event_interruptible(button_waitq, ev_press);
 }
 /*把中断标识清零*/
 ev_press = 0;
 /*一组键值被传递到用户空间*/
 err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
 return err ? -EFAULT : min(sizeof(key_values), count);
}
static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
 unsigned int mask = 0;
 /*把调用poll 或者select 的进程挂入队列,以便被驱动程序唤醒*/
 poll_wait(file, &button_waitq, wait);
 if (ev_press)
  mask |= POLLIN | POLLRDNORM;
 return mask;
}
/*设备操作集*/
static struct file_operations dev_fops = {
 .owner = THIS_MODULE,
 .open = s3c24xx_buttons_open,
 .release = s3c24xx_buttons_close,
 .read = s3c24xx_buttons_read,
 .poll = s3c24xx_buttons_poll,
};
static struct miscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};
/*设备初始化,主要是注册设备*/
static int __init dev_init(void)
{
 int ret;
 /*把按键设备注册为misc 设备,其设备号是自动分配的*/
 ret = misc_register(&misc);

 if(ret < 0)
  {
        printk(DEVICE_NAME "register falid!\n");
        return ret;
   }
 printk (DEVICE_NAME"\tinitialized\n");
 return 0;
}
/*注销设备*/
static void __exit dev_exit(void)
{
 misc_deregister(&misc);
}
module_init(dev_init); //模块初始化,仅当使用insmod/podprobe 命令加载时有用,如果设备不是通过模块方式加载,此处将不会被调用
module_exit(dev_exit); //卸载模块,当该设备通过模块方式加载后,可以通过rmmod 命令卸载,将调用此函数
MODULE_LICENSE("GPL"); //版权信息
MODULE_AUTHOR("singleboy."); //作者名字

Edit the Kconfig file:

 config SMDK2440_BUTTONS
  tristate "Buttons driver for SMDK2440 development boards"
  depends on SMDK2440_CPU2440
  default y if SMDK2440_CPU2440
  help
   this is buttons driver for SMDK2440 development boards

Also change the Makefile:

obj-$(CONFIG_SMDK2440_BUTTONS)  += smdk2440_buttons.o

Make the kernel out and verify it on board.

Test code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

int main(void)
{
 int buttons_fd;
 char buttons[6] = {'0', '0', '0', '0', '0', '0'}; //定义按键值变量,对于驱动函数中的key_values 数组

 buttons_fd = open("/dev/buttons", 0); /*打开按键设备/dev/buttons*/
 if (buttons_fd < 0) {
  perror("open device buttons"); /*打开失败则退出*/
  exit(1);
 }

 for (;;) { /*永读按键并打印键值和状态*/
  char current_buttons[6];
  int count_of_changed_key;
  int i;

/*使用read 函数读取一组按键值(6 个)*/
  if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {
   perror("read buttons:");
   exit(1);
  }

/*逐个分析读取到的按键值*/

  for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {
   if (buttons[i] != current_buttons[i]) {
    buttons[i] = current_buttons[i];

/*打印按键值,并标明按键按下/抬起的状态*/
    printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
    count_of_changed_key++;
   }
  }
  if (count_of_changed_key) {
   printf("\n");
  }
 }

 close(buttons_fd); /*关闭按键设备文件*/
 return 0;
}

Test Result:

/root # ./buttons
key 1 is down
key 1 is up
key 2 is down
key 2 is up
key 3 is down
key 3 is up
key 4 is down