字符设备驱动点亮led

字符设备驱动——点亮led

1 代码

1.1 驱动代码

led_cdev.c

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


//定义一个宏表示设备名字
#define dev_name "led_chrdev"

//定义宏表示设备数量
#define dev_count (3)

//定义字符设备号(普通定义)
static dev_t devno;

//用于创建文件
struct class *led_chrdev_class;



//定义一个led结构体用来
static struct led_chrdev
{
    //包含字符结构体
    struct cdev dev;

    //定义变量接收虚拟地址
    unsigned int __iomem *va_dr;
    unsigned int __iomem *va_gdir;
    unsigned int __iomem *va_iomuxc_mux;
    unsigned int __iomem *va_ccm_ccgrx;
    unsigned int __iomem *va_iomux_pad;

    //定义变量存储物理地址
    unsigned long pa_dr;
    unsigned long pa_gdir;
    unsigned long pa_iomuxc_mux;
    unsigned long pa_ccm_ccgrx;
    unsigned long pa_iomux_pad;

    //引脚的编号
    unsigned int led_pin;
    unsigned int clock_offset;

};



static struct led_chrdev led_cdev[dev_count] = {
   
    {.pa_dr = 0x0209C000,.pa_gdir = 0x0209C004,.pa_iomuxc_mux =\
	 0x20E006C,.pa_ccm_ccgrx = 0x20C406C,.pa_iomux_pad =\
	 0x20E02F8,.led_pin = 4,.clock_offset = 26},
	{.pa_dr = 0x20A8000,.pa_gdir = 0x20A8004,.pa_iomuxc_mux =\
	 0x20E01E0,.pa_ccm_ccgrx = 0x20C4074,.pa_iomux_pad =\
	 0x20E046C,.led_pin = 20,.clock_offset = 12},
	{.pa_dr = 0x20A8000,.pa_gdir = 0x20A8004,.pa_iomuxc_mux =\
	 0x20E01DC,.pa_ccm_ccgrx = 0x20C4074,.pa_iomux_pad =\
	 0x20E0468,.led_pin = 19,.clock_offset = 12},
     
};



static int led_chrdev_open(struct inode *inode, struct file *filp);
static int led_chrdev_release(struct inode *inode, struct file *filp);
static ssize_t led_chrdev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos);


static struct file_operations  led_chrdev_fops = 
{
    .owner = THIS_MODULE,
    .open = led_chrdev_open,
    .release = led_chrdev_release,
    .write = led_chrdev_write,
  
};



static int led_chrdev_open(struct inode *inode, struct file *filp)
{
    unsigned int val = 0;
    //定义led结构体指针变量led_cdev
    struct led_chrdev *led_cdev = (struct led_chrdev *)container_of(inode->i_cdev,struct led_chrdev,dev);

    filp->private_data =container_of(inode->i_cdev,struct led_chrdev,dev);

    printk("open sucess!\n");

    //将物理地址转为虚拟地址
    led_cdev->va_dr = ioremap(led_cdev->pa_dr,4);
    led_cdev->va_gdir = ioremap(led_cdev->pa_gdir,4);
    led_cdev->va_iomuxc_mux = ioremap(led_cdev->pa_iomuxc_mux,4);
    led_cdev->va_ccm_ccgrx = ioremap(led_cdev->pa_ccm_ccgrx,4);
    led_cdev->va_iomux_pad = ioremap(led_cdev->pa_iomux_pad,4);


    //操作各虚拟地址内的数据
    val = ioread32(led_cdev->va_ccm_ccgrx);
    val &= ~(3 << led_cdev->clock_offset);
    val |= (3 << led_cdev->clock_offset);
    iowrite32(val,led_cdev->va_ccm_ccgrx);

    iowrite32(5,led_cdev->va_iomuxc_mux);

    iowrite32(0x1F838,led_cdev->va_iomux_pad);

    val = ioread32(led_cdev->va_gdir);
    val &= ~(1 << led_cdev->led_pin);
    val |= (1 << led_cdev->led_pin);
    iowrite32(val,led_cdev->va_gdir);

    val = ioread32(led_cdev->va_dr);
    val |= (0X01 << led_cdev->led_pin);
    iowrite32(val,led_cdev->va_dr);


    return 0;
}



static int led_chrdev_release(struct inode *inode, struct file *filp)
{

    struct led_chrdev *led_cdev =(struct led_chrdev *)container_of(inode->i_cdev, struct led_chrdev,dev);
    //将映射的虚拟地址释放
    iounmap(led_cdev->va_dr);
    iounmap(led_cdev->va_gdir);
    iounmap(led_cdev->va_iomuxc_mux);
    iounmap(led_cdev->va_ccm_ccgrx);
    iounmap(led_cdev->va_iomux_pad);

    printk("release sucess!\n");
    return 0;
}





static ssize_t led_chrdev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos)
{
    unsigned long val = 0;
    unsigned long ret = 0;

    int tmp = count;

    //将一个字符串转换成一个无符号长整型的数据\
    实现user到内核的数据拷贝,将buf里字符串转为10进制的无符号数,存在ret中
    kstrtoul_from_user(buf,tmp,10,&ret);

    struct led_chrdev * led_cdev = (struct led_chrdev*)filp->private_data;

    val = ioread32(led_cdev->va_dr);
    if(ret == 0)
        val &= ~(0X01 << led_cdev->led_pin);
    else
        val |= (0X01 << led_cdev->led_pin);

    iowrite32(val,led_cdev->va_dr);

    *ppos += tmp;

    return tmp;
}




static  __init int led_chrdev_init(void)
{
    int i = 0;
    dev_t cur_dev;
    
    printk("led chrdev init!\n");


    int ret = 0;
    //采用动态分配的方式,获取主设备编号,次设备号为0,设备名称和设备数量
    //设备名称(char_dev)可通过命令cat /proc/devices查看
   
    ret = alloc_chrdev_region(&devno, 0, dev_count, dev_name);
    if (ret < 0) {
        printk("fail to alloc devno\n");
        goto alloc_err;
    }

    //创建设备文件
    led_chrdev_class = class_create(THIS_MODULE,"led_chrdev");

    for(;i < dev_count;i++)
    {
        //字符设备结构体cdev与文件操作结构体file_operations关联起来
        cdev_init(&led_cdev[i].dev, &led_chrdev_fops);
        led_cdev[i].dev.owner = THIS_MODULE;

        //设备号
        cur_dev = MKDEV(MAJOR(devno),MINOR(devno)+i);

   
        //添加设备至cdev_map散列表中
        ret = cdev_add(&led_cdev[i].dev, cur_dev, 1);
        if (ret < 0) {
            printk("fail to add cdev\n");
        goto add_err;
        }

        //创建设备文件
        device_create(led_chrdev_class,NULL,cur_dev,NULL,dev_name"%d",i);


    }
   

     return 0;

	add_err:
   		//添加设备失败时,注销设备号
   		unregister_chrdev_region(devno, dev_count);

	alloc_err:
   		return ret;
 
}




static void __exit led_chrdev_exit(void)
{

    int i;
    dev_t cur_dev;

    printk("char_device exit!\n");
    
    for(i = 0;i < dev_count;i++)
    {
        cur_dev = MKDEV(MAJOR(devno),MINOR(devno)+i);

        //销毁设备文件
        device_destroy(led_chrdev_class,cur_dev);

        //将cdev结构体从内核中移除
        cdev_del(&led_cdev[i].dev);
    }

    //注销设备号
    unregister_chrdev_region(devno,dev_count);
    
    class_destroy(led_chrdev_class);
}



module_init(led_chrdev_init);
module_exit(led_chrdev_exit);
MODULE_LICENSE("GPL");

1.2 应用代码

led_test.c代码,用来测试驱动程序。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

char *led_on = "0\n";
char *led_off = "1\n";

int main(void)
{
   printf("led_cdev led start!!!\n");
   
   //打开设备文件
   int fd = open("/dev/led_chrdev0", O_RDWR);
   if(fd > 0)
      printf("led_chrdev0 open success,red led on!\n");
   else
      printf("led_chrdev0 open fail!\n");
   
   //写入数据,red on
   write(fd, led_on, strlen(led_on));
   //写入完毕,关闭文件
   close(fd);
   sleep(1);



   //打开设备文件
   fd = open("/dev/led_chrdev1", O_RDWR);
   if(fd > 0)
      printf("led_chrdev1 open success,green led on!\n");
   else
      printf("led_chrdev1 open fail!\n");
   
   //写入数据,green on
   write(fd, led_on, strlen(led_on));
   //写入完毕,关闭文件
   close(fd);
   sleep(1);




   //打开设备文件
   fd = open("/dev/led_chrdev2", O_RDWR);
   if(fd > 0)
      printf("led_chrdev2 open success,blue red on!\n");
   else
      printf("led_chrdev2 open fail!\n");
   
   //写入数据,blue on
   write(fd, led_on, strlen(led_on));
   //写入完毕,关闭文件
   close(fd);
   sleep(1);




   //关闭设备
   fd = open("/dev/led_chrdev0", O_RDWR);
   write(fd, led_off, strlen(led_off));
   close(fd);

   fd = open("/dev/led_chrdev1", O_RDWR);
   write(fd, led_off, strlen(led_off));
   close(fd);

   fd = open("/dev/led_chrdev2", O_RDWR);
   write(fd, led_off, strlen(led_off));
   close(fd);


   
   return 0;
}

1.3 Makefile文件

#这里具体要根据当前文件和内核所在目录的位置设定
KERNEL_DIR=../ebf_linux_kernel/build_image/build

ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE

obj-m := led_cdev.o
out =  led_test

all:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
	$(CROSS_COMPILE)gcc -o $(out) led_test.c

.PHONY:clean
clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
	rm $(out)

2 程序运行结果

2.1 编译文件

使用make命令使用Makefile编译文件

make

将编译好的led_test和led_cdev.ko文件,通过NFS移动到开发板

image-20221201091735663

2.2 加载驱动模块

使用命令加载模块,可以看到我们驱动文件编写时设置的打印信息led chrdev init!

sudo insmod /mnt/led_cdev.ko

hhhh

驱动模块加载成功后,在/proc/devices目录下,会生成一个字符设备led_chrdev,主设备号为244。

cat /proc/devices

image-20221201091943850

2.3 查看设备文件

查看/dev/目录下,创建的设备文件,在应用程序中打开

ls /dev/le*

image-20221130170549958

2.4 运行测试程序

运行led_test可执行文件,这里我将led_test文件拷贝到当前目录了。

sudo ./led_test

执行应用程序,屏幕上打印信息,且开发板依次红灯、绿灯、蓝灯亮。最后关闭。

image-20221201090704105

2.5 卸载内核模块

使用命令卸载

sudo rmmod led_cdev

image-20221201092439386

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值