上篇文章分析了天嵌科技附带的led驱动程序的核心语句
s3c2410_gpio_cfgpin(gpio_table[i], gpio_cfg_table[i]);
s3c2410_gpio_setpin(gpio_table[i], 0);
本文将驱动改写了引脚配置方式,采用直接写虚拟地址的方法进行引脚配置及引脚电平读写操作。
另外,本文使用标准字符设备方式实现了led驱动,驱动程序比起使用杂项设备(misc_device)要稍复杂一些,但其操作更标准些,因此更适合于驱动程序的学习
下面贴出驱动程序及测试程序代码:
#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/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 <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define DEVICE_NAME "LED_DEV"
MODULE_DESCRIPTION("My kernel module");
MODULE_AUTHOR("traveler");
MODULE_LICENSE("Dual BSD/GPL");
dev_t led_dev_num;
struct cdev *led_cdev;
int led_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long flag);
int led_open(struct inode *inode,struct file *filp);
int led_release(struct inode *inode,struct file *filp);
static struct file_operations led_fops =
{
.owner = THIS_MODULE,
.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
};
int led_open(struct inode *inode,struct file *filp)
{
return 0;
}
int led_release(struct inode *inode,struct file *filp)
{
return 0;
}
int led_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long flag)
{
unsigned int gpio_base = (unsigned int)S3C24XX_VA_GPIO;
unsigned int gpbdat = gpio_base + 16 + 4;
if(flag>4 || cmd >1)
return -1;
if(cmd == 1)//on
{
//将GPBDAT寄存器指定位置0
(*(unsigned int *)gpbdat) &= ~(0x020 << flag);
printk("<1>ioctl led[%ld]-on[%d].\n",flag,cmd);
}
else
{
//将GPBDAT寄存器指定位置1
(*(unsigned int *)gpbdat) |= (0x020 << flag);
printk("<1>ioctl led[%ld]-off[%d].\n",flag,cmd);
}
return 0;
}
static int leds_init_module(void)
{
int ret;
unsigned int gpio_base = (unsigned int )S3C24XX_VA_GPIO;
unsigned int gpbdat = gpio_base + 16 + 4;
unsigned int gpbcon = gpio_base + 16 ;
led_cdev = NULL;
ret = alloc_chrdev_region(&led_dev_num,0,1,DEVICE_NAME);
if(ret<0)
{
printk("<1>get device register failure!\n");
return ret;
}
printk("<1>Module leds init,major:%d,minor:%d\n",MAJOR(led_dev_num),MINOR(led_dev_num) );
led_cdev = kmalloc(sizeof(struct cdev),GFP_KERNEL);
if(!led_cdev)
{
unregister_chrdev_region(led_dev_num,1);
return -ENOMEM;
}
memset(led_cdev,0,sizeof(struct cdev));
led_cdev->owner = THIS_MODULE;
led_cdev->ops = &led_fops;
cdev_init(led_cdev,&led_fops);
ret = cdev_add(led_cdev,led_dev_num,1);
if(ret)
{
kfree(led_cdev);
unregister_chrdev_region(led_dev_num,1);
return -1;
}
//配置GPBCON寄存器
(*(unsigned int *)gpbcon) &= ~(0x0ff<<10);
(*(unsigned int *)gpbcon) |= (0x055 << 10) ;
//熄灭所有led
(*(unsigned int *)gpbdat) |= (0x0e0 );
return ret;
}
static void leds_exit_module(void)
{
cdev_del(led_cdev);
kfree(led_cdev);
led_cdev = NULL;
unregister_chrdev_region(led_dev_num,1);
led_dev_num = 0;
printk("<1>Module leds exit\n" );
}
module_init(leds_init_module);
module_exit(leds_exit_module);
使用insmod加载驱动程序之后,要根据printk得到的设备号建立字符设备文件节点/dev/LED_DEV
测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
int fd;
int i;
fd = open("/dev/LED_DEV", 0);
if (fd < 0) {
perror("open device leds");
exit(1);
}
for(i=0;i<64;i++)
{
ioctl(fd, i&0x01, 0);
ioctl(fd, (i&0x02)>>1, 1);
ioctl(fd, (i&0x04)>>2, 2);
ioctl(fd, (i&0x08)>>3, 3);
usleep(200000);
}
close(fd);
return 0;
}