很久没有继续写博客了,主要是最近一段时间在友善之臂开发板mini2440上调试驱动程序。按照配套的教材我分别看完按键驱动和led灯的驱动,突然想将两者结合起来,即写一个新的驱动,在该驱动中实现按键控制led灯的功能,简而言之,是将两者驱动融合,在内核空间实现此功能,然后在用户空间编写应用程序来测试所写的驱动。熟悉一下驱动的编写和移植流程。经过一段时间的努力,终于实现了此功能。写驱动的基本流程
(1)看原理图,熟悉硬件的IO端口接线,查看相关芯片说明手册。列出相关寄存器的表格
(2)驱动的编写尽量在已有驱动的基础上改动,切忌从头编写
(3)编写相应的Makefile ,根据要求修改该驱动程序所在目录的Makefile和Konfig文件,然后在源代码的根目录下执行make menuconfig
新编写的驱动会在其中显示。保存退去后,执行make modules 即可生成相应驱动的文件,后缀为.ko(有错误需修改驱动或Makefile,直到没有错误为止)下载到开发板上即可 详细的步骤见下面的介绍。
驱动源代码如下:
#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>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#define DEVICE_NAME "buttons_leds_zhao"
#define IOCTL_LED_ON 1
#define IOCTL_LED_OFF 0
struct button_irq_desc {
int irq;
int pin;
int pin_setting;
int number;
char *name;
};
static struct button_irq_desc button_irqs [] = {
{IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8 , 0, "KEY0"},
{IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"},
{IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"},
{IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"},
{IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"},
{IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"},
};
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int ev_press = 0;//the flag of the interupt event ,the value that is 1 shows that the interupt has happend
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
int down;
其中函数s3c2410_gpio_getpin等在linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach/hardware.h找得到。它的功能是得到相应的管脚值,一般一个管脚有四种功能有关叫32位寄存器中2位来控制。驱动写好之后,接下来我们要做的工作是修改Kconfig和Makefile。
因为我所写的这个驱动属于字符驱动,因此将它放在/linux-2.6.32.2/drivers/char目录下
在该目录下修改Kconfig文件
config MINI2440_HELLO_MODULE
tristate "Mini2440 module sample"
depends on MACH_MINI2440
default m if MACH_MINI2440
help
Mini2440 module sample.
config BUTTONS_LEDS_ZHAO
tristate "Mini2440 button and leds sample"
depends on MACH_MINI2440
default m if MACH_MINI2440
help
Mini2440 button and leds module sample.
添加上面BUTTONS_LEDS_ZHAO一段,这一段的作用是我们在源代码目录下输入命令
make menuconfig 后的菜单显示的一个条目。保存退出后,在 /linux-2.6.32.2/drivers/char下修改
Makefile文件,上面修改的Kconfig文件只是在菜单配置的界面出现该条目,如果不修改该目录下Makefile,仍然不能编译该驱动。Makefile的修改如下:
obj-$(CONFIG_MINI2440_BUZZER) += mini2440_pwm.o
obj-$(CONFIG_MINI2440_ADC) += mini2440_adc.o
obj-$(CONFIG_BUTTONS_LEDS_ZHAO) += button_leds_zhao.o
添加了最下面一行。将该驱动选择为模块编译方式
但是在源代码目录下执行make modules 依然不能生成button_leds_zhao.ko,原来在我的源代码目录下[root@localhost linux-2.6.32.2]# ls
arch config_mini2440_vga1024x768 firmware kernel Module.symvers security
block COPYING fs lib net sound
config_mini2440_a70 CREDITS include MAINTAINERS README tools
config_mini2440_l80 crypto init Makefile REPORTING-BUGS usr
config_mini2440_n35 Documentation ipc mm samples virt
config_mini2440_t35 drivers Kbuild modules.order scripts
由于新解压的包,当中配置文件根据显示屏的不同有好几种,我的显示屏是NEC的 因此执行命令 cp config_mini2440_n35 .config 后在执行该命令就可以生成了。具体原因会在下一篇博客中讨论.config, Kconfig, Makefile 讨论
下载到开发板上去后,执行modprobe button_leds_zhao 就可以了,注意不要带后缀.ko
编写的测试程序如下;
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#include<errno.h>
int main()
{
int i,button_fd;
char key_values[6]={'0','0','0','0','0','0'};
char temp='0';
button_fd=open("/dev/buttons_leds_zhao",0);
if(button_fd<0)
{
perror("cannot open device buttons");
exit(1);
}
while(1)
{
fd_set rds;
int ret,io;
FD_ZERO(&rds);
FD_SET(button_fd,&rds);
ret=select(button_fd+1,&rds,NULL,NULL,NULL);
if(ret<0)
{
perror("select");
exit(1);
}
if(0==ret)
{
printf("time out/n");
}
else if (FD_ISSET(button_fd,&rds))
{
int ret=read(button_fd,key_values,sizeof(key_values));
if(ret!=sizeof(key_values))
{
if(errno!=EAGAIN)
perror("read button/n");
continue;
}
else
{
for(i=0;i<6;i++)
if(key_values[i]!='0')
{
printf("K %d is %s/n",i+1,
key_values[i]=='0'?"released":"pressed down ");
if(temp==key_values[i])
{
if(i>=0&&i<4)
ioctl(button_fd,0,i);//light off
printf("light off/n");
temp='0';
}
//i means the number of the six buttons,but we only control four lights here
else
{
if(i>=0&&i<4)
ioctl(button_fd,1,i);//light on
printf("light on/n");
temp=key_values[i];
}
printf("temp=%c /n",temp);
}
}
}
}
close(button_fd);
return 0;
}
测试程序在编译以后,一直达不到理想的要求,按下一个键后,灯亮,但是再按其他的键没有反应。多次修改后。终于实现功能。即按下一个键,相应的灯亮,再按下该键,灯灭。按其他的键有同样的效果。
经过这次的调试,掌握了最简单驱动的编写和移植。
// udelay(0);
down = !s3c2410_gpio_getpin(button_irqs->pin);
if (down != (key_values[button_irqs->number] & 1)) { // Changed
key_values[button_irqs->number] = '0' + down;
ev_press = 1;
wake_up_interruptible(&button_waitq);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
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;
}
for(i=0;i<sizeof(led_cfg_table)/sizeof(led_cfg_table[0]);i++)
{
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
}
// ev_press = 1;
return 0;
}
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;
}
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)
return -EAGAIN;
else
wait_event_interruptible(button_waitq, ev_press);
}
ev_press = 0;//clear the flag of the interupt every time after read a time
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_wait(file, &button_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static int s3c24xx_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
if(arg>4)
return -EINVAL;
switch(cmd)
{
case IOCTL_LED_ON:
s3c2410_gpio_setpin(led_table[arg],0);
// printk("light on/n");
break;
case IOCTL_LED_OFF:
s3c2410_gpio_setpin(led_table[arg], 1);
// printk("light off/n");
break;
default:
return -EINVAL;
}
return 0;
}
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,
.ioctl = s3c24xx_leds_ioctl,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
ret = misc_register(&misc);
printk (DEVICE_NAME"/tinitialized/n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
这个驱动主要是将两个单独的驱动结合起来。