驱动之路二----LED(MISC设备)

驱动开始之路------LED驱动


在上面的这篇博客中,我写的LED驱动是将LED作为字符设备写的,大家可能也发现了,在写驱动文件时特别冗长,这样是不利于程序员们写驱动的,Linux内核(2.6版内核之后的)添加了设备分类的概念,注意与设备模型概念的区分。


设备模型:(三要素)总线、驱动、设备

      优点:电源管理(设计的初衷)

                 热插拔

                 增强驱动的可移植性


Linux 内核将所有的设备分为三大类:字符设备、块设备、网络设备、

要记住,Linux 内核只是给出了这种分类,并没有明确给出哪种设备是属于哪一类,这根据程序员自己的经验来判定,什么样的设备拿什么样类来写;

字符设备:是设备种类最多的一类,基本常见的设备都可以拿字符设备来写

块设备:很容易看出,主要是存储类的,SD卡,NAND,硬盘等

网络设备:常见的就是网卡了


但是在这三大类的基础上,是可以写了,但是还是会有诸多不便,2.6版本以后的内核又加上了设备分类


设备分类:实际将字符设备进一步分类了

        输入设备
        fb设备
        sound设备
        ....
        tty设备
        ....
        misc

当然也可以自己创建类,通过函数class_create创建一个类


了解完了基本概念,我们现在将led作为misc设备重写,

基本的宏定义我就不重写了,都与本文开始提到的博客中的头文件相同,只是将led_info结构体修改一下,

struct led_info {
	void __iomem *v;
	int status;
	int user;
	dev_t no;
	
	struct miscdevice dev;//misc设备的结构体
	
	void (*on)(struct led_info *l, int no);
	void (*off)(struct led_info *l, int no);
};
对于设备文件基本就不需要改了,对于一个相同的设备,设备文件基本不动

为了体现完整性,我将驱动文件完全的写以下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include "s3c_led.h"

void s3c_led_on(struct led_info *l, int no)
{
        u32 tmp;

        tmp = readl(l->v + GPMDAT);
        tmp &= GPM_LOW(no);
        writel(tmp, l->v + GPMDAT);
}

void s3c_led_off(struct led_info *l, int no)
{
        u32 tmp;

        tmp = readl(l->v + GPMDAT);
        tmp |= GPM_HIGH(no);
        writel(tmp, l->v + GPMDAT);
}

void s3c_led_init(struct led_info *l)
{
        u32 tmp;

        tmp = readl(l->v + GPMCON);
        tmp &= GPM0_3_MASK;
        tmp |= GPM0_3_OUT;
        writel(tmp, l->v + GPMCON);

        tmp = readl(l->v + GPMDAT);
        tmp |= GPM0_3_HIGH;
        writel(tmp, l->v + GPMDAT);

        l->on = s3c_led_on;
        l->off = s3c_led_off;
}

void s3c_led_exit(struct led_info *l)
{
        u32 tmp;

        tmp = readl(l->v + GPMDAT);
        tmp |= GPM0_3_HIGH;
        writel(tmp, l->v + GPMDAT);
}

//-----------------------------------------------------------以下是与硬件无关的,模式比较固定
struct led_info *led;

ssize_t led_read(struct file *fp, char __user *buffer, size_t count, loff_t *off)
{
	char buf[64];
	int ret = 0;
	
	ret += sprintf(buf, "State:%x\n", led->status);
	if(copy_to_user(buffer, buf, ret))
		return -EFAULT;
	
	return ret;
}

ssize_t led_write(struct file *fp, const char __user *buffer, size_t count, loff_t *off)
{
	if(buffer[1] == '1'){
		led->on(led, buffer[0] - '0');
		led->status |= S_LED_ON(buffer[0] - '0');
	}else if(buffer[1] == '0'){
		led->off(led, buffer[0] - '0');
		led->status &= S_LED_OFF(buffer[0] - '0');
	}else{
		return -EPERM;
	}

	return count;
}

int led_open(struct inode *no, struct file *fp)
{
	if(!led->user)
		led->user++;	
	else
		return -EBUSY;

	return 0;
}

int led_release(struct inode *no, struct file *fp)
{
	if(led->user)
		led->user--;
	else
		return -ENODEV;

	return 0;
}

struct file_operations led_ops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.read = led_read,
	.write = led_write,
};

int led_probe(struct platform_device *pdev)
{
	struct resource *led_res;
	int ret;

	led_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if(!led_res)
		return -EBUSY;

	led = kzalloc(sizeof(struct led_info), GFP_KERNEL);
	if(!led)
		return -ENOMEM;
	
	led->v = ioremap(led_res->start, led_res->end - led_res->start + 1);
	if(!led->v){
		ret = -ENOMEM;
		goto remap_v_error;
	}
//可以看出,此处的代码比使用字符设备要少很多
	led->dev.name = pdev->name;
	led->dev.minor = MISC_DYNAMIC_MINOR;//动态分配次设备号,misc设备的主设备号固定是10
	led->dev.fops = &led_ops;

	ret = misc_register(&led->dev);/这里注册的是misc设备,就不是字符设备了
	if(ret)
		goto misc_register_error;

	s3c_led_init(led);
	led->status = S_LED_OFF_ALL;
	
	return 0;
misc_register_error:
	iounmap(led->v);
remap_v_error:
	kfree(led);
	return ret;
}

int led_remove(struct platform_device *pdev)
{
	s3c_led_exit(led);
	misc_deregister(&led->dev);
	iounmap(led->v);
	kfree(led);

	return 0;
}

struct platform_driver drv = {
	.probe = led_probe,
	.remove = led_remove,
	.driver = {
		.name = "s3c-led",
	},
};

static __init int module_test_init(void)
{
	return platform_driver_register(&drv);
}

static __exit void module_test_exit(void)
{
	platform_driver_unregister(&drv);
}

module_init(module_test_init);
module_exit(module_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Musesea");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("Test for module");

作为misc设备的LED驱动就写完了。

大家如果发现什么问题一定要告诉我,大家一起学习了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值