LINUX字符型设备驱动 三.LED驱动

1.LED.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/uaccess.h>

static int major = 99;
static int minor = 0;
static dev_t devno;
static struct class *cls;
static struct device *test_device;

#define PB_CFG 0x01c20824 //PB口控制寄存器
#define PB_DAT 0x01c20834 //PB口数据寄存器
#define DEVICE_MINOR_NUM 2
#define DEVICE_NAME "led"

static int *PBcfg;
static int *PBdat;

void fs4412_led_off(int num) //关灯
{
    switch (num)//这里只要将管脚对应的位置1即可如pb4 那么或上0001 0000即可
    {
    case 1:
        iowrite32(ioread32(PBdat) | (1<<4),PBdat);//置高关灯
        break;
    case 2:
        iowrite32(ioread32(PBdat) | (1<<5),PBdat);  
        break;
    default:
        break;
    }
}

void fs4412_led_on(int num) //开灯程序
{
    switch (num)//这里只要将管脚对应的位置0即可如pb4 那么与上1110 1111即可
    {
    case 1:
        iowrite32(ioread32(PBdat) &(~(1<<4)),PBdat);//置低开灯
        break;
    case 2:
        iowrite32(ioread32(PBdat) &(~(1<<5)),PBdat);//置低开灯
        break;
    default:
        fs4412_led_off(1);//如果输入错误那么都关灯
        fs4412_led_off(2);
        break;
    }
}

static int led_open(struct inode *inode, struct file *filep)//文件打开时默认都关灯
{ //open
    fs4412_led_off(1);
    fs4412_led_off(2);
    return 0;
}

static int led_release(struct inode *inode, struct file *filep)//文件关闭时默认都关灯
{ //close
    fs4412_led_off(1);
    fs4412_led_off(2);
    return 0;
}

static ssize_t led_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)
{
    return 0;
}

static ssize_t led_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)
{
    int led_num;

    if (len != 4)
    {
        return -EINVAL;
    }
    if (copy_from_user(&led_num, buf, len))
    {
        return -EFAULT;
    }

    fs4412_led_on(led_num);
    //printk(KERN_INFO "led_num =%d 
", led_num);
    return 0;
}

static struct file_operations hello_ops =
    {
        .open = led_open,
        .release = led_release,//当执行close()函数时会调用release()
        .read = led_read,
        .write = led_write,
};

static void fs4412_led_init(void)
{
    PBcfg = ioremap(PB_CFG, 4); //映射到物理地址
    PBdat = ioremap(PB_DAT, 4);
    //配置io口为输出模式   //先左移12位取反得 FFFF0FFF 相与 再或上 0x1<<12  ioread32就是调用readl
    writel((ioread32(PBcfg) & ~(7 << 16)) | (1 << 16), PBcfg); //PB4 设定out
    writel((ioread32(PBcfg) & ~(7 << 20)) | (1 << 20), PBcfg); //PB5 设定out
}

static int led_init(void)
{
    int ret;
    if (major)//静态申请方法
    {
        devno = MKDEV(major, minor);
        ret = register_chrdev(major,DEVICE_NAME, &hello_ops);//
    }
    else//动态申请方法
    {
        ret=alloc_chrdev_region(&devno,minor,DEVICE_MINOR_NUM,DEVICE_NAME);//这个好像还没有添加file_operation
        major=MAJOR(devno);
        minor=MINOR(devno);
        ret = register_chrdev(major,DEVICE_NAME, &hello_ops);
        printk(KERN_INFO "alloc region %d 
",major);
    }
    cls = class_create(THIS_MODULE, "myclass");//创建类
    if (IS_ERR(cls))
    {
        unregister_chrdev(major, DEVICE_NAME);
        printk(KERN_ERR "class create failed 
");
        return -EBUSY;
    }
    printk(KERN_INFO "class create successs 
");
    //创建设备节点
    test_device = device_create(cls, NULL, devno, NULL, DEVICE_NAME); //mknod /dev/led
    if (IS_ERR(test_device))
    {
        class_destroy(cls);
        unregister_chrdev(major,DEVICE_NAME);
        printk(KERN_ERR "create /dev/led failed 
");
        return -EBUSY;
    }
    printk(KERN_INFO "create /dev/led successs 
");
    fs4412_led_init(); //初始化io
    return 0;
}

void fs4412_led_unmap(void)//内存取消映射
{
    iounmap(PBcfg);
    iounmap(PBdat);
}

static void led_exit(void)
{
    fs4412_led_unmap();//先取消映射
    device_destroy(cls, devno);
    class_destroy(cls);
    unregister_chrdev(major,DEVICE_NAME);
    printk(KERN_INFO "led_exit
");
}

MODULE_AUTHOR("Zheng qihang <zqh18268832867@gmail.com>");
MODULE_DESCRIPTION("LED driver for V3s controllers");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("module:leds-V3s");
module_init(led_init);
module_exit(led_exit);

2.main.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
    int led_fd,i,lednum;
    led_fd=open("/dev/led",O_RDWR);
    if(led_fd<0)
    {
        perror("open failed!
");
        return -1;
    }
    for(i=0;i<10;i++)//循环10次
    {
        lednum=0;
        write(led_fd,&lednum,sizeof(int));
        lednum=i%2+1;
        write(led_fd,&lednum,sizeof(int));
        sleep(1);
    }
    close(led_fd);
    return 0;
}

3.Makefile

#General Purpose Makefile for cross compile Linux Kernel module
ifneq ($(KERNELRELEASE),)

obj-m := led.o  #+=是连接字符串

else

OBJ := led
ARCH := arm    
CROSS_COMPILE := /usr/local/arm/arm-linux-gnueabihf-4.9/bin/arm-linux-gnueabihf-
KERN_DIR := /home/zqh/lichee/linux-zero-4.14.y  #选择内核路径
SOURCE := main.c
TARGET := test_led
PWD =$(shell pwd)  #当前路径
FILE_PWD=$(strip $(PWD))
LICHEDIR := /root/led_driver/
all:
        make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERN_DIR) M=$(PWD) modules
        $(CROSS_COMPILE)gcc $(SOURCE) -o $(TARGET)
.PHONY : clean   
clean :                                
        rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order .*.o.ko.cmd .*.ko.cmd .*.mod.o.cmd .*.o.cmd $(TARGET)        
install:
        scp $(FILE_PWD)/$(OBJ).ko root@172.24.41.12:$(LICHEDIR)
        scp $(FILE_PWD)/$(TARGET) root@172.24.41.12:$(LICHEDIR)
endif
原文地址:https://www.cnblogs.com/ZQQH/p/8681917.html