linux 驱动 dev_info,不大一般的LED驱动 - Linux驱动实践_Linux编程_Linux公社-Linux系统门户网站...

看了这么多内核代码,终于要自己开始做驱动了.按照由易到难,由浅入深的顺序,就从LED开始.

LED驱动可以说是hello world之后最简单的驱动模块了.如果自己写一个LED驱动那是很简单的,其实用linux内核中的leds子系统来做也是比较简单的,内核中的leds子系统是将led抽象成platform_device,并有leds_class.这样,在/sys/class/leds/目录下面就可以利用sysfs文件系统来实现LED的操作.其实,也可以在此基础上添加/dev访问的接口,甚至用不着包含mknod的脚本和udev等工具,在模块注册后就可以生成/dev下的设备文件.

一步一步的来,首先利用platform虚拟总线来实现sysfs文件系统下的LED操作.

在mach-smdk2440.c中新增platform_device如下:

static struct platform_device smdk_led5 = {

.name  = "s3c24xx_led",

.id  = 0,

.dev  = {

.platform_data = &smdk_pdata_led5,

.release = &platform_led_release,

},

};

对于具体的板子,是GPB5-8对应四个LED,所以这里smdk_pdata_led5的定义如下:

static struct s3c24xx_led_platdata smdk_pdata_led5 = {

.gpio  = S3C2410_GPB(5),

.flags  = S3C24XX_LEDF_ACTLOW ,//| S3C24XX_LEDF_TRISTATE,

.name  = "led5",

//.def_trigger = "nand-disk",

};

接着在static struct platform_device *smdk2440_devices[] __initdata中新增&smdk_led5,这样系统在启动时就会由smdk2440_machine_init调用platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));来添加刚才加上的platform_device.

看完了platform_device再看下platform_driver是如何注册的:

在drivers/leds目录下的Makefile中有obj-$(CONFIG_LEDS_S3C24XX)+= leds-s3c24xx.o

所以在make menuconfig的时候记得将其选为M.

这样,注册leds-s3c24xx.ko就会在/sys/class/leds中有

led5  led6  led7  led8四个目录,进入led5目录,有

brightness      device          power          trigger

dev            max_brightness  subsystem      uevent

执行echo 0 > brightness就可以关闭led,而echo 1 > brightness就可以打开led.

因为有leds_class,而在led-class.c中leds-init函数中有leds_class->dev_attrs = led_class_attrs;

其中,led_class_attrs定义如下

static struct device_attribute led_class_attrs[] = {

__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),

__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),

#ifdef CONFIG_LEDS_TRIGGERS

__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),

#endif

__ATTR_NULL,

};

根据sysfs的属性文件,读写属性文件最终调用的就是show和store函数,在这里就是led_brightness_show和led_brightness_store函数.

顺着调用其实设置寄存器的动作是在leds-s3c24xx.c中的s3c24xx_led_set实现的.从这里可以看出,LEDS这个子系统也做了抽象,与平台相关的放在一个文件中,而与平台无关的抽象出来放在led-class中.

到此,就可以通过sysfs文件系统来访问led设备了.

那么,开头说的在此基础如何做/dev访问的接口呢?

在leds目录下新建一个文件led-dev.c

在Makefile中添加obj-$(CONFIG_LEDS_INTF_DEV)+= led-dev.o

记得在Kconfig文件中也要添加对应的信息.

led-dev.c文件内容如下:

/*

* LEDS subsystem, dev interface

*

* Copyright (C) 2012  Baikal

* Author: Baikal

*

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License version 2 as

* published by the Free Software Foundation.

*/

#include

#include

#include

#include

#include

#include

#include "leds.h"

static dev_t led_devt;

#define LED_DEV_MAX 8  /*actually,there is 4 leds on the 2440 board*/

static int led_dev_open(struct inode *inode, struct file *file)

{

printk(KERN_INFO "debug by baikal: led dev open\n");

printk(KERN_INFO "i_rdev:0x%x\n",inode->i_rdev);

printk(KERN_INFO "i_rdev:%d\n",inode->i_rdev);

printk(KERN_INFO "i_rdev:major:%d minor:%d\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));

struct led_classdev *led = container_of(inode->i_cdev,

struct led_classdev, char_dev);

file->private_data = led;

return 0;

}

static ssize_t

led_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)

{

printk(KERN_INFO "debug by baikal: led dev read\n");

}

static ssize_t

led_dev_write(struct file *file, char __user *buf, size_t count, loff_t *ppos)

{

char data;

printk(KERN_INFO "debug by baikal: led dev write\n");

copy_from_user(&data,buf,count);

if(data == '0')

led_set_brightness(file->private_data,0);

else

led_set_brightness(file->private_data,1);

}

static const struct file_operations led_dev_fops = {

.owner  = THIS_MODULE,

.read  = led_dev_read,

.write  = led_dev_write,

.open  = led_dev_open,

// .release = led_dev_release,

};

void led_dev_prepare(struct led_classdev *led)

{

static int minor = 0;

printk(KERN_INFO "debug by baikal: led dev pre\n");

led_devt = MKDEV(254,minor); //add by baikal

minor++;

led->dev->devt = MKDEV(MAJOR(led_devt), MINOR(led_devt));

cdev_init(&led->char_dev, &led_dev_fops);

//led->char_dev.owner = led->owner;

}

void led_dev_add_device(struct led_classdev *led)

{

printk(KERN_INFO "debug by baikal: led dev add \n");

if (cdev_add(&led->char_dev, led->dev->devt, 1))

printk(KERN_WARNING " failed to add char device ");

else

pr_debug("%s: dev (%d:)\n", led->name,

MAJOR(led_devt));

}

void led_dev_del_device(struct led_classdev *led)

{

if (led->dev->devt)

cdev_del(&led->char_dev);

}

void __init led_dev_init(void)

{

/*

int err;

err = alloc_chrdev_region(&led_devt, 0, LED_DEV_MAX, "led");

if(err < 0)

printk(KERN_ERR "%s: failed to allocate char dev region\n",__FILE__);

*/

}

void __exit led_dev_exit(void)

{

/*

if(led_devt)

unregister_chrdev_region(led_devt, LED_DEV_MAX);

*/

}

并在led-class.c中的led_classdev_register函数的开头处添加

static int minor = 0;

dev_t led = MKDEV(254,minor); //add by baikal

printk(KERN_INFO "debug by baikal: led class reg 1\n");

led_cdev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);

if (led_cdev->dev == NULL) {

return -ENOMEM;

}

led_dev_prepare(led_cdev);              //add by baikal

printk(KERN_INFO "debug by baikal: led class reg 2\n");

minor++;

并将led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,

"%s", led_cdev->name);

改为led_cdev->dev = device_create(leds_class, parent, led, led_cdev,

"%s", led_cdev->name);

在函数的结尾处再调用 led_dev_add_device(led_cdev);

这样,在模块注册后在/dev目录下就有

[root@BaikalHu led5]# ls /dev/l*

/dev/led5  /dev/led7  /dev/loop0  /dev/loop2  /dev/loop4  /dev/loop6

/dev/led6  /dev/led8  /dev/loop1  /dev/loop3  /dev/loop5  /dev/loop7

这样就在/dev下生成了设备文件.

可以照下面的方式来测试/dev下的led设备文件的功能:

#include

#include

#include

#include

#include

#include

int main(int argc, char **argv)

{

int fd;

if(argc != 3)

{

printf("usage : ./led led5-8 on/off\n");

return -1;

}

char file[20] = "/dev/";

strcat(file, argv[1]);

fd = open(file, O_RDWR);

if(fd < 0)

{

perror("open led");

return -1;

}

if(!strcmp(argv[2],"on"))

{

write(fd,"1",1);

}

else if(!strcmp(argv[2],"off"))

{

write(fd,"0",1);

}

close(fd);

return 0;

}

测试一切正常.

回头解释下为什么这样就可以自己生成/dev下的设备文件,原因在于在新内核中的device_add函数中有这么一段:

if (MAJOR(dev->devt)) {

error = device_create_file(dev, &devt_attr);

if (error)

goto ueventattrError;

error = device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

devtmpfs_create_node(dev);

}

看到devtmpfs_create_node了没,这就是答案.如果对devtmpfs有兴趣的话,以前写的《linux设备模型之字符设备》大概提到了devtmpfs.

通过自己的添加,可以用sysfs和dev两种接口方式来访问LED.但是实际设置寄存器的函数都是同一处地方.

linux下的驱动,最终还是要操作寄存器的,但是与裸机不同的是linux将硬件设备抽象为文件,那我们就必须按照这种机制来书写linux下的驱动,机制的根本是文件系统和设备模型,但是可以通过不同的策略来达到目的,比如sysfs和devtmpfs.0b1331709591d260c1c78e86d0c51c18.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值