1. 程序思路:
通过ioctrl 来将应用程序的命令传递到驱动中的 struct file_operation 中的 unlocked_ioctl 方法中,判断命令进行相应的操作。
在通用的头文件中定义ioctrl的命令
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',2)
我们想在应用程序中这样来操作led
int main(int argc, const char *argv[])
{
int fd = open("/dev/led", O_RDWR); // 打开设备节点
if(fd<0) {
perror("open");
return -1;
}
puts("app open end");
while(1) {
ioctl(fd, LED_ON); // 打开灯
sleep(2);
ioctl(fd, LED_OFF); // 关闭灯
sleep(2);
}
close(fd);
puts("app close end");
return 0;
}
那驱动中我们就要实现字符设备的 struct file_operation的 unlocked_ioctl 方法
static long led_ioctl (struct file *filep, unsigned int cmd, unsigned long argv)
{
switch(cmd)
{
case LED_ON:
printk("LED_ON\n");
led_on();
break;
case LED_OFF:
printk("LED_OFF\n");
led_off();
break;
default:
return -EINVAL;
}
return 0;
}
struct file_operations led_fops = {
.owner=THIS_MODULE,
.open = led_open,
.release =led_release,
.unlocked_ioctl = led_ioctl,
};
当然,对于要操作硬件IO,需要将IO寄存器ioremap地址空间映射到内核空间上来。。
在4412开发板上的实现的led可以控制的驱动代码如下:
驱动: fs4412_led.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include "fs4412_led.h"
#include <asm/io.h>
#define LED_MA 250
#define LED_MI 0
#define GPX2CON 0x11000c40
#define GPX2DAT 0x11000c44
MODULE_LICENSE("GPL");
static unsigned int * gpx2con =NULL;
static unsigned int * gpx2dat =NULL;
struct cdev cdev;
dev_t devno=MKDEV(LED_MA, LED_MI);
static void led_on(void)
{
writel(readl(gpx2dat) |(0x1<<7),gpx2dat);
return ;
}
static void led_off(void)
{
writel(readl(gpx2dat) & (~(0x1<<7)) ,gpx2dat);
return ;
}
static int led_open (struct inode * inodep, struct file *filep)
{
printk("led_open\n");
return 0;
}
static int led_release (struct inode *inodep, struct file *filep)
{
printk("led_release\n");
return 0;
}
static long led_ioctl (struct file *filep, unsigned int cmd, unsigned long argv)
{
switch(cmd)
{
case LED_ON:
printk("LED_ON\n");
led_on();
break;
case LED_OFF:
printk("LED_OFF\n");
led_off();
break;
default:
return -EINVAL;
}
return 0;
}
struct file_operations led_fops = {
.owner=THIS_MODULE,
.open = led_open,
.release =led_release,
.unlocked_ioctl = led_ioctl,
};
static int led_init(void)
{
unsigned int ret = 0;
printk("led_init start \n");
ret = register_chrdev_region(devno, 1, "fs4412_led_device");
if(ret<0)
{
printk("register_chrdev_region error\n");
return ret;
}
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &led_fops);
ret = cdev_add(&cdev, devno, 1);
if(ret < 0)
{
printk("cdev_add error\n");
goto err1;
}
gpx2con = ioremap(GPX2CON, 4);
if(NULL == gpx2con)
{
printk("ioremap(GPX2CON) error\n");
goto err2;
}
gpx2dat = ioremap(GPX2DAT, 4);
if(NULL == gpx2dat)
{
printk("ioremap(GPX2DAT) error\n");
goto err3;
}
writel((readl(gpx2con) & (~(0xf<<28))) | (0x1<<28) ,gpx2con);
writel(readl(gpx2dat) & (~(0x1<<7)) ,gpx2dat);
printk("led_init end \n");
return 0;
err3:
iounmap(gpx2con);
err2:
cdev_del(&cdev);
err1:
unregister_chrdev_region(devno,1);
return ret;
}
static void led_exit(void)
{
printk("led_exit start \n");
iounmap(gpx2dat);
iounmap(gpx2con);
cdev_del(&cdev);
unregister_chrdev_region(devno,1);
printk("led_exit end \n");
return ;
}
module_init(led_init);
module_exit(led_exit);
头文件:fs4412_led.h
#ifndef __FS4412_LED_H__
#define __FS4412_LED_H__
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',2)
#endif
Makefile 文件
$(warning KERNELRELEASE=$(KERNELRELEASE))
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /home/qin/linux-3.14-fs4412/
#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
arm-linux-gcc led_test.c -o led_test.out
cp fs4412_led.ko led_test.out /source/rootfs/
clean:
rm -rf *.out *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
else
obj-m := fs4412_led.o
endif
应用层测试用的代码: led_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "fs4412_led.h"
int main(int argc, const char *argv[])
{
int fd = open("/dev/led", O_RDWR); // 打开设备节点
if(fd<0) {
perror("open");
return -1;
}
puts("app open end");
while(1) {
ioctl(fd, LED_ON); // 打开灯
sleep(2);
ioctl(fd, LED_OFF); // 关闭灯
sleep(2);
}
close(fd);
puts("app close end");
return 0;
}
2. 可能出现的问题:
[root@lip ~]# rmmod
rmmod: can’t change directory to ‘/lib/modules’: No such file or directory
[root@lip ~]# mkdir /lib/modules
[root@lip ~]# rmmod led
rmmod: can’t change directory to ‘3.4.112’: No such file or directory
[root@lip ~]# mkdir /lib/modules/3.4.112
[root@lip ~]# rmmod led
led_exit is load
这个问题是删除模块提示 can’t change directory to ‘/lib/modules’: No such file or directory 错误,解决的办法就是 提示不能 改变到 /lib/modules ,是因为没有这个文件夹,我们新建这个文件夹,又提示没有 3.4.112 这个文件夹,我们继续新建这个文件夹,解决。
3. 用到platform总线的 led 驱动:
根据platform的结构,将设备的资源单独放到设备中,在设备中
在模块的入口函数中注册一个platform的设备
在驱动侧注册驱动结构体
其中的 platform_driver.driver.name 的名字 跟 platform_device.name 做比对。probe方法中实现
- io 资源的映射
- 注册字符设备,创建设备节点
remove方法中进行反操作
- 设备节点注销
- 类注销
- 字符设备注销