字符设备驱动模型分为三步:
第一步:驱动设备初始化 (分配cdev、初始化cdev、注册cdev、硬件初始化)
第二步:实现设备操作(读写以及控制设备)
第三步: 驱动注销
虽然这是一个最简单的驱动,而且之前也曾经写过两遍,时隔大半年,今天回过头来重新整还是一波三折!感谢那些热心无私的同学的帮助!
先贴下原理图和相关芯片手册上的一些要用到的东西!
这里LED连接的GPIO引脚 输出高电平是LED关 输出低电平LED亮, LED电路部分的这里就不截图了
然后去查看这个GPM的相关寄存器:
这里贴上代码:
led.c
#include
#include
#include
#include
#include /* writel */
#include"led.h"
#define GPMCON 0x7F008820 //LED对应的GPIO控制寄存器地址
#define GPMDATA 0x7F008824
unsigned int *led_config;//LED控制寄存器
unsigned int *led_data;//LED数据寄存器 0表示打开LED 1表示关闭LED
struct cdev cdev_led; //这里定义一个cdev结构体变量
dev_t devno; //用来申请主设备号的变量
//这里硬件操作放到led_open函数中
//控制LED 设置GPIO为输出模式(放到open中) 然后控制GPIO(放到ioctl中)
int led_open(struct inode *node, struct file *filp)
{
//unsigned int reg;
led_config = ioremap(GPMCON, 4);//将物理地址转化为虚拟地址,4个字节
//reg = readl(led_config);
//reg |= 0x1111;
writel(0x1111, led_config);//将值写入配置寄存器 设置为输入
led_data = ioremap(GPMDATA, 4);//将物理地址转化为虚拟地址,4个字节
return 0;
}
//第一步定义命令 第二步实现命令 命令一般放在.h头文件中
long led_icotl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case LED_ON:
writel(0x00,led_data);//向32位寄存器中写入一个值
return 0;
case LED_OFF:
writel(0xFF,led_data);//向32位寄存器中写入一个值
return 0;
default:
return -EINVAL;
}
}
static const struct file_operations led_fops =
{
.open = led_open,
.unlocked_ioctl = led_icotl,
};
/*
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
*/
static int led_init()
{
cdev_init(&cdev_led, &led_fops);//初始化cdev_led结构;两个参数
alloc_chrdev_region(&devno, 0, 1, "myled");//分配主设备号 采用动态分配方式
cdev_add(&cdev_led, devno, 1);//注册主设备号
return 0;
}
static void led_exit()
{
cdev_del(&cdev_led);//第一步 注销设备
unregister_chrdev_region(devno, 1);//第二步 将申请到的主设备号也注销掉
}
module_init(led_init);
module_exit(led_exit);
led.h
//点亮 熄灭 这里定义两个 而且都不需要传参 具体可以结和前一课
#define LED_MAGIC 'L'
#define LED_ON _IO(LED_MAGIC, 0)
#define LED_OFF _IO(LED_MAGIC, 1)
obj-m := led.o
KDIR := /home/kernel/linux-ok6410
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
#include
#include
#include
#include
#include
#include
#include"led.h"
int main(int argc, char *argv[])
{
int fd;
int cmd;
if(argc < 2)
{
printf("please enter the second para! \n");
return 0;
}
cmd = atoi(argv[1]);
fd = open("/dev/myled", O_RDWR);
if(cmd == 1)
ioctl(fd, LED_ON);
else
ioctl(fd, LED_OFF);
return 0;
}
同上一篇一样,这里目标文件通过nfs同步到开发板!
insmod led.ko
然后cat /proc/devices 查看myled(上面程序中给led字符设备取的名字)所对应的设备号
然后 maknod /dev/myled c 设备号 0
然后运行应用程序 ./led_app 1 点亮led!
这里发张点亮led的效果图片:
顺便说说前面我驱动程序中应为粗心写错了一个地方:
针对上面的程序相信看懂了吧!然后运行应用程序时就出现了这样的情况!
这里面有很多有用的信息可以供我们分析错误,这里发一个链接,别人写的一篇博客 根据上面的信息来分析问题找出原因所在:http://blog.csdn.net/kangear/article/details/8217329
点击打开链接