上面是我的微信和QQ群,欢迎新朋友的加入。
本来是为点灯准备的驱动,不过现在还没正常操作IO口,就是分配节点和设备号
驱动代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define ON 0
#define OFF 1
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO5_IO08_BASE (0x02290028)
#define SW_PAD_GPIO5_IO08_BASE (0x0229006c)
#define GPIO5_DR_BASE (0X020AC000)
#define GPIO5_GDIR_BASE (0X020AC004)
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO5_IO08;
static void __iomem *SW_PAD_GPIO5_IO08;
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;
#define DEVICE_NAME "board_led"
#define DEVICE_CNT 1
static int major = 0; /* 静态设备号方式的默认值 */
static int minor = 0; /* 静态设备号方式的默认值 */
module_param(major, int, S_IRUGO);
module_param(minor, int, S_IRUGO);
struct cdev *led; /* cdev 数据结构 */
static dev_t devno; /* 设备编号 */
static struct class *led_class;
void led_switch(u8 sta)
{
u32 val = 0;
if(sta == ON) {
val = readl(GPIO5_DR);
val &= ~(1 << 8);
writel(val, GPIO5_DR);
}else if(sta == OFF) {
val = readl(GPIO5_DR);
val|= (1 << 8);
writel(val, GPIO5_DR);
}
}
static int led_open(struct inode *inode, struct file *file )
{
printk(KERN_INFO DEVICE_NAME " opened!\n");
return 0;
}
static int led_release(struct inode *inode, struct file *file )
{
printk(KERN_INFO DEVICE_NAME " closed!\n");
return 0;
}
static ssize_t led_read(struct file *file, char *buf,size_t count, loff_t *f_pos)
{
printk(KERN_INFO DEVICE_NAME " read method!\n");
return count;
}
static ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *f_pos)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
//retvalue = copy_from_user(databuf, buf, count);
//if(retvalue < 0) {
// printk("kernel write failed!\r\n");
// return -EFAULT;
//}
//ledstat = databuf[0]; /* 获取状态值 */
//if(ledstat == ON) {
// led_switch(ON); /* 打开 LED 灯 */
//} else if(ledstat == OFF) {
// led_switch(OFF); /* 关闭 LED 灯 */
//}
return count;
}
//static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static long led_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
printk(KERN_INFO DEVICE_NAME " ioctl method!\n");
return 0;
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
.open = led_open,
.release = led_release,
.unlocked_ioctl = led_ioctl
};
static int __init led_init(void)
{
u32 val = 0;
int ret;
///* 初始化 LED */
///* 1、寄存器地址映射 */
//IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
//SW_MUX_GPIO5_IO08 = ioremap(SW_MUX_GPIO5_IO08_BASE, 4);
//SW_PAD_GPIO5_IO08 = ioremap(SW_PAD_GPIO5_IO08_BASE, 4);
//GPIO5_DR = ioremap(GPIO5_DR_BASE, 4);
//GPIO5_GDIR = ioremap(GPIO5_GDIR_BASE, 4);
///* 2、使能 GPIO1 时钟 */
//val = readl(IMX6U_CCM_CCGR1);
//val &= ~(3 << 26); /* 清除以前的设置 */
//val |= (3 << 26); /* 设置新值 */
//writel(val, IMX6U_CCM_CCGR1);
///* 3、设置 GPIO1_IO03 的复用功能,将其复用为
//* GPIO1_IO03,最后设置 IO 属性。
//*/
//writel(5, SW_MUX_GPIO5_IO08);
///* 寄存器 SW_PAD_GPIO5_IO08 设置 IO 属性 */
//writel(0x10B0, SW_PAD_GPIO5_IO08);
///* 4、设置 GPIO1_IO03 为输出功能 */
//val = readl(GPIO5_GDIR);
//val &= ~(1 << 8); /* 清除以前的设置 */
//val |= (1 << 8); /* 设置为输出 */
//
//writel(val, GPIO5_GDIR);
///* 5、默认关闭 LED */
//val = readl(GPIO5_DR);
//val |= (0 << 8);
//writel(val, GPIO5_DR);
/* 注册字符设备驱动 */
if (major > 0) { /* 静态设备号 */
devno = MKDEV(major, 0);
ret = register_chrdev_region(devno, DEVICE_CNT, DEVICE_NAME);
} else { /* 动态设备号 */
ret = alloc_chrdev_region(&devno, 0, DEVICE_CNT, DEVICE_NAME); /* 从系统获取主设备号 */
major = MAJOR(devno);
minor = MINOR(minor);
}
if (ret < 0) {
printk(KERN_ERR "cannot get major %d \n", major);
return -1;
}
printk(KERN_ALERT " major:%d! minor:%d\n",major,minor);
led = cdev_alloc(); /* 分配 led 结构 */
if (led != NULL) {
cdev_init(led, &led_fops); /* 初始化 led 结构 */
led->owner = THIS_MODULE;
if (cdev_add(led, devno, DEVICE_CNT) != 0) { /* 增加 led 到系统中 */
printk(KERN_ERR "add cdev error!\n");
goto error;
}
} else {
printk(KERN_ERR "cdev_alloc error!\n");
return -1;
}
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(led_class)) {
printk(KERN_INFO "create class error\n");
return -1;
}
device_create(led_class, NULL, devno, NULL, DEVICE_NAME);
printk(KERN_ALERT "mod init!\n");
return 0;
error:
unregister_chrdev_region(devno, DEVICE_CNT); /* 释放已经获得的设备号 */
return ret;
}
static void __exit led_exit(void)
{
/* 取消映射 */
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO5_IO08);
iounmap(SW_PAD_GPIO5_IO08);
iounmap(GPIO5_DR);
iounmap(GPIO5_GDIR);
/* 注销字符设备 */
cdev_del(led);/*移除字符设备*/
unregister_chrdev_region(devno,DEVICE_CNT);/*释放设备号*/
device_destroy(led_class,devno);
class_destroy(led_class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jun,2839084093@qq.com");
makefile
obj-m := board_led.o
KERNEL_SRC := /home/jun/nuc980/nuc980bsp/linux-master
SRC := $(shell pwd)
all:
$(MAKE) -C $(KERNEL_SRC) M=$(SRC)
modules_install:
$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
clean:
rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
rm -f Module.markers Module.symvers modules.order
rm -rf .tmp_versions Modules.symvers
测试代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#define DEV_NAME "/dev/board_led"
int main(int argc, char *argv[])
{
int i;
int fd = 0;
int dat = 0;
fd = open (DEV_NAME, O_RDWR);
if (fd < 0) {
perror("Open "DEV_NAME" Failed!\n");
exit(1);
}
i = read(fd, &dat, 1);
if (!i) {
perror("read "DEV_NAME" Failed!\n");
exit(1);
}
dat = 0;
i = write(fd, &dat, 1);
if (!i) {
perror("write "DEV_NAME" Failed!\n");
exit(1);
}
close(fd);
return 0;
}
//arm-linux-gcc board_led_test.c -o board_led_test
编译生成可执行文件
先加载驱动,再运行测试代码
自动分配成功
能进入读写操作