项目场景:
记录linux platform驱动学习
1.选用mini6410核心板
2.使用LED作为驱动测试,LED0-3分别为GPK4-7
3.本记录使用insmod方式加载驱动,并没有加入内核中,直接在内核中加入驱动更为简单
(记录分为3步:device(平台设备),driver(设备驱动),app(应用测试))
驱动注册流程:平台设备---->设备驱动—>APP测试
step1.平台设备-device:
首先进行平台设备注册
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#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 <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-k.h>
#include <plat/devs.h>
//MAP.H内定义所有GPIO地址0x7F008000 这里因为LED是GPK 所以自己定义地址
#define S3C64XX_PA_GPK (0x7F008800)
#define S3C64XX_SZ_GPK 0xC
static void my_plat_gpio_release(struct device * dev);
static int my_plat_gpio_init(void);
static void my_plat_gpio_exit(void);
/* 平台资源的定义 */
static struct resource my_plat_gpio_resource[] = {
[0] = {
.start = S3C64XX_PA_GPK,
.end = S3C64XX_PA_GPK+S3C64XX_SZ_GPK-1,
.flags = IORESOURCE_MEM,
}
};
//资源整合
struct platform_device my_plat_gpio_device = //此处不要定义为static的,否则会有错误
{
.name = "my_plat_gpio",
.id = -1,
.num_resources = ARRAY_SIZE(my_plat_gpio_resource),
.resource = my_plat_gpio_resource,
.dev = {
.release = my_plat_gpio_release, },//也许可以不要 没验证
};
//释放 感觉没有实质作用
static void my_plat_gpio_release(struct device * dev)
{
printk("remove device\n");
}
//初始化
static int my_plat_gpio_init(void)
{
platform_device_register(&my_plat_gpio_device);
return 0;
}
//注销
static void my_plat_gpio_exit(void)
{
platform_device_unregister(&my_plat_gpio_device);
}
module_init(my_plat_gpio_init);
module_exit(my_plat_gpio_exit);
MODULE_LICENSE("GPL");
makefile 编译:
obj-m :=led_device.o
KERNELDIR :=/opt/FriendlyARM/mini6410/linux/linux-2.6.38
all:
make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul*
step2.设备驱动-driver:
设备驱动代码
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/ioctl.h>
#include<linux/types.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/fs.h>
#include <mach/map.h>
#include <asm/io.h> //包含了IO内存的相关操作
#include <linux/platform_device.h>
#define DEVICE_NAME "my_plat_gpio"
#define LED_ON 1
#define LED_OFF 0
dev_t devid;
//led GPK 4 5 6 7
volatile unsigned long *gpkcon;
volatile unsigned long *gpkdat;
volatile unsigned long *gpkup;
//open
static int leds_open(struct inode *inode , struct file *file)
{
unsigned tmp;
tmp=ioread32(gpkcon);
mb();
tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);
iowrite32(tmp,gpkcon);
mb(); //设置读写操作屏障,保证读写按照当前所写的顺序进行
tmp=ioread32(gpkup);
mb(); //设置读写操作屏障,保证读写按照当前所写的顺序进行
tmp=(tmp & ~(0xffU<<4))|(0xaaU<<4);
iowrite32(tmp,gpkup);
return 0;
}
//ioctl
static long leds_ioctl( struct file *filp,unsigned int cmd,
unsigned long arg)
{
unsigned tmp;
if(arg>4) return -EINVAL;
tmp = ioread32(gpkdat);
mb();
printk("my platform led:%d,on:%d\n",arg,cmd);
printk("tmp:0x%x,gpkdat:0x%x\n",tmp,gpkdat);
switch(cmd)
{
case LED_ON: tmp &= ~(1<<(arg+4)); iowrite32(tmp,gpkdat);
return 0;
case LED_OFF: tmp |= 1<<(arg+4); iowrite32(tmp,gpkdat);
return 0;
default: return -EINVAL;
}
}
//接口定义
static struct file_operations leds_ops = {
.owner = THIS_MODULE,
.open = leds_open,
.unlocked_ioctl = leds_ioctl,
};
static struct cdev *cdev_led;
static struct class *led_class;
static struct resource *ret;
//探测
static int led_probe(struct platform_device *pdev)
{
int msize,val;
ret = platform_get_resource(pdev,IORESOURCE_MEM, 0);
msize = ret->end - ret->start + 1;
printk("ret->start:0x%x,msize:0x%x\n",ret->start,msize);
if(!request_mem_region(ret->start,msize,DEVICE_NAME))
printk("request con error\n");
else
{
gpkcon=ioremap_nocache(ret->start,msize);
printk("request con success \ngpkcon:0x%x\n",gpkcon);
}
gpkdat = gpkcon + 2;
gpkup = gpkcon + 3;
printk("gpkdat:0x%x,gpkup:0x%x\n",gpkdat,gpkup);
val = alloc_chrdev_region(&devid,0,1,DEVICE_NAME);
if(val) return -1;
cdev_led = cdev_alloc();
cdev_init(cdev_led,&leds_ops);
val = cdev_add(cdev_led,devid,1);
if(val)
{
printk(KERN_INFO "Add device led error!\n");
return -1;
}
led_class = class_create(THIS_MODULE,DEVICE_NAME);
device_create(led_class,NULL,devid,NULL,"%s",DEVICE_NAME);
printk(KERN_INFO "LED Initilized I'm in! ^_^ \n");
return 0;
}
//平台设备驱动移除
static int led_remove(struct platform_device *pdev)
{
iounmap(gpkcon);
release_mem_region(ret->start,(ret->end - ret->start + 1));
cdev_del(cdev_led);
device_destroy(led_class,devid);
class_destroy(led_class);
unregister_chrdev_region(devid, 1);
return 0;
}
//平台设备资源接口
struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.owner = THIS_MODULE,
.name = DEVICE_NAME,
},
};
//平台设备驱动注册
static int __init leddrv_init(void)
{
platform_driver_register(&led_driver);
return 0;
}
//平台设备驱动注销
static void __exit leddrv_exit(void)
{
platform_driver_unregister(&led_driver);
}
module_init(leddrv_init);
module_exit(leddrv_exit);
MODULE_LICENSE("GPL");
makefile 编译:
obj-m :=led_driver.o
KERNELDIR :=/opt/FriendlyARM/mini6410/linux/linux-2.6.38
all:
make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul*
step3.应用测试:
APP代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
//arm-linux-gcc led.c -o led
int main(int argc, char **argv)
{
int on;
int led_no;
int fd;
/* 检查led控制的两个参数,如果没有参数输入则退出。*/
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);
}
/*打开/dev/leds设备文件*/
fd = open("/dev/my_plat_gpio", 0);
if (fd < 0) {
perror("my gpio");
exit(1);
}
printf("open dev success\n");
/*通过系统调用ioctl和输入的参数控制led*/
ioctl(fd, led_no, on);
/*关闭设备句柄*/
close(fd);
return 0;
}
arm-linux-gcc led.c -o led 编译生成led应用程序
点亮led0 ./led 1 0
因为是普通IO驱动修改而来, cmd \arg顺序相反,arg不能写2问题有需要自行将2个参数互换,测试通过就懒得改了.
软、硬件解释:
1、
leds_ioctl、leds_open:
因为LED使用GPK4\5\6\7
根据手册所示,寄存器设置为代码所示
2、
设备驱动:led_probe内:
volatile unsigned long *gpkcon;
volatile unsigned long *gpkdat;
volatile unsigned long *gpkup;
gpkdat = gpkcon + 2;
gpkup = gpkcon + 3;
根据寄存器所示,指针+2+3时地址分别为对应寄存器
gpkcon=ioremap_nocache(ret->start,msize);
将0x7F008800寄存器地址映射到ram中的一块线性地址访问.和io_remap有些类似
3、
首先insmod led_device.ko 进行平台设备注册
然后 insmod led_driver.ko 安装设备驱动
最后 ./led 1 1 点亮led1 测试完成
通过终端查看 ls /sys/bus/platform/device
通过终端查看 ls /sys/bus/platform/driver
这里都具有my_plat_gpio则说明已经将驱动与平台设备关联成功
通过终端查看 ls /dev发现有my_plat_gpio节点
因为动态分配设备设备号,所以此处不需要mknod再生成节点
应用程序直接调用此节点即可