喜羊羊系列之【设备 - 驱动 编入内核】

博客:http://blog.csdn.net/muyang_ren

*针对不同的平台,可能放进的不是下面的文件内,只做参考

1、头文件

放进:linux-3.0.8\arch\arm\plat-samsung\include\plat

led.h

#ifndef _HEAD_H
#define _HEAD_H

#define MAGIC 'h'

#define LED_ON 		_IOW(MAGIC,1,int)
#define LED_OFF 	_IOW(MAGIC,0,int)

struct led_device{
 	dev_t 			devno;
	unsigned int 	led_major;
	struct cdev	 	*led_cdev;
	struct class 	*led_class;
	struct device 	*led_device;
};
#endif

====================================================================================================================================

2、设备文件

方法一:将设备资源直接加进/linux-3.0.8/arch/arm/mach-s5pv210下的mach-smdkv210.c

①
struct platform_device s5pv210_led_device_lhy = {
	.name			= 	"s5pv210_led_lhy",
	.id			= 	1,
};

static struct platform_device *smdkv210_devices[] __initdata = {
。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
②将设备信息加入总线
<span style="white-space:pre">	</span>修改arch/arm/mach-s5pv210/mach-smdkv210.c文件
		static struct platform_device *smdkv210_devices[] __initdata = {
			...
			...
			/*添加如下代码*/
			&s5pv210_led_device_lhy,    //新添加的
		}

方法二:

①将设备文件dev-led.c 放进 linux-3.0.8/arch/arm/plat-samsung

led_dev.c

#include <linux/platform_device.h>
#include <plat/led.h>
#include <plat/devs.h>
#include <plat/cpu.h>

struct platform_device s5pv210_led_device_lhy = {
	.name			= 	"s5pv210_led_lhy",
	.id				= 	1,
};

②向arch/arm/mach-s5pv210/mach-smdkv210.c(跟平台架构相关文件)添加

static struct platform_device *smdkv210_devices[] __initdata = {
	....
	&s5pv210_led_device_lhy,     //新添加
};

③向linux-3.0.8/arch/arm/plat-samsung/Makefile添加

	obj-$(CONFIG_S3C_DEV_LED)       += led_dev.o

④向linux-3.0.8/arch/arm/plat-samsung/Kconfig添加

	config S3C_DEV_LED
        bool "S5PV210 LED  driver support"
        help
             s5pv210 led device support

⑤添加外部声明arch/arm/plat-samsung/include/plat/devs.h

	extern struct platform_device s5pv210_led_device_lhy;

====================================================================================================================================

3、平台驱动

①将led_drv.c 放进linux-3.0.8/drivers/my_led

led_drv.c

#include<linux/fs.h>		//register_chrled
#include<linux/device.h>	//class_create/ledice_create
#include<linux/slab.h>		//kmalloc
#include<asm/uaccess.h>		//copy_to_user/copy_from_user
#include<asm/io.h>			//ioremap
#include<linux/gpio.h>		//gpio_request
#include <plat/gpio-cfg.h>	//s3c_gpio_cfgpin
#include <linux/cdev.h>     //cdev_alloc
#include <linux/platform_device.h>

//以下是移植时需要增加的
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <plat/cpu.h>
#include <plat/led.h>
#include <plat/devs.h>

static struct led_device *led_drv;

static int led_open(struct inode *inode, struct file *file)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);

	s3c_gpio_cfgpin(S5PV210_GPC0(3),S3C_GPIO_OUTPUT);
	s3c_gpio_cfgpin(S5PV210_GPC0(4),S3C_GPIO_OUTPUT);

	return 0;
}

static ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
	return count;
}

ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
	return 0;
}

static long led_ioctl(struct file *file, unsigned int cmd, unsigned long val)
{
	printk(KERN_INFO"%s()-%d\n", __func__, __LINE__);
	printk(KERN_INFO"cmd=%d arg=%ld\n", cmd, val);

	switch(cmd)
	{
		case LED_ON:
			gpio_set_value(S5PV210_GPC0(val),1);
			break;
		case LED_OFF:
			gpio_set_value(S5PV210_GPC0(val),0);
			break;
		default:
			break;
	}
	return 0;
}

//硬件操作方法
static struct file_operations led_fops={
		.owner	= THIS_MODULE,
		.open   = led_open,
		.write  = led_write,
		.read   = led_read,
		.unlocked_ioctl = led_ioctl,
};

static int s5pv210_led_probe(struct platform_device *pdrv){
	int ret;
	led_drv = kmalloc(sizeof(struct led_device),GFP_KERNEL);
	if(led_drv==NULL){
		printk(KERN_ERR"no memory malloc for fs210_led\n");
		return -ENOMEM;
	}

	/*1. 动态注册/申请主设备*/
	ret=alloc_chrdev_region(&led_drv->devno,0,1,"dev_module");
	if (ret < 0) {
		printk(KERN_ERR "unable to get major\n");
		return -EFAULT;
		goto out_err_1;
	}	

	//从设备号中分离出主设备号
	led_drv->led_major = MAJOR(led_drv->devno);
	/*为cdev分配空间*/
	led_drv->led_cdev  = cdev_alloc();
	/*注册硬件操作方法/初始化cdev*/
	cdev_init(led_drv->led_cdev,&led_fops);
	/*注册字符设备*/
	cdev_add(led_drv->led_cdev,led_drv->devno,1);

	/*2. 创建设备类*/
	led_drv->led_class=class_create(THIS_MODULE,"led_class");
	if (IS_ERR(led_drv->led_class)) {
		printk(KERN_ERR "class_create() failed for led_class\n");
		ret = -ENODATA;
		goto out_err_2;
	}

	/*3. 创建设备文件*/
	led_drv->led_device=device_create(led_drv->led_class,NULL,MKDEV(led_drv->led_major,0),NULL,"led"); //   /led/xxx
	if (IS_ERR(led_drv->led_device)) {
		printk(KERN_ERR "device_create failed for led_device\n");
		ret = -ENODEV;
		goto out_err_3;
	}

	/*申请GPC0_3,4引脚资源*/
	gpio_request(S5PV210_GPC0(3),"LED1");
	gpio_request(S5PV210_GPC0(4),"LED2");

	return 0;
out_err_3:
	class_destroy(led_drv->led_class);
out_err_2:
	unregister_chrdev(led_drv->led_major,"led_module");
out_err_1:
	kfree(led_drv);
	return ret;

}
static int s5pv210_led_remove(struct platform_device *pdrv){
	unregister_chrdev(led_drv->led_major,"led_module");
	device_destroy(led_drv->led_class,MKDEV(led_drv->led_major,0));
	class_destroy(led_drv->led_class);
	gpio_free(S5PV210_GPC0(3));
	gpio_free(S5PV210_GPC0(4));
	kfree(led_drv);
	return 0;
}

struct platform_device_id led_ids[]={
	[0]={
		.name = "s5pv210_led_lhy",
		.driver_data = 0,
	},
};

static struct platform_driver s5pv210_led_driver = {
	.probe	= s5pv210_led_probe,
	.remove = s5pv210_led_remove,
	.driver = {		.name = "s5pv210_led_lhy",
		.owner = THIS_MODULE,	},
	.id_table = led_ids,
}; 

static int __devinit s5pv210_led_init(void)
{
	return platform_driver_register(&s5pv210_led_driver);
}

static void __devexit s5pv210_led_exit(void)
{
	platform_driver_unregister(&s5pv210_led_driver);
}

module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

MODULE_DESCRIPTION("LED driver for Marvell PM860x");
MODULE_AUTHOR("kiron");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s5pv210-led");

②在当前目录的Kconfig后添加,没有就新建

	config S5PV210_LED_DRV
    		tristate  "led_dev  for fs210 device"
    		help
        		led driver is for s5pv210, choose y/m/n

③在当前目录的Makefile后添加,没有就新建

<span style="white-space:pre">	</span>obj-$(CONFIG_S5PV210_LED_DRV) = led_drv.o

④修改上级目录的Makefile和Kconfig

将linux-3.0.8/drivers/Kconfig  添加

	source "drivers/my_led/Kconfig"

将linux-3.0.8/drivers/Makefile 添加

<span style="white-space:pre">	</span>obj-y        += mydriver/

最后就是自己make menuconfig里配置选项了。

====================================================================================================================================

4、测试程序

编译测试要使用交叉工具连

<span style="white-space:pre">	</span>arm-none-linux-gnueabi-gcc led_test.c -o led_test

附:

将可执行文件加入到开机启动,修改根文件系统filesystem

vi  filesystem/etc/init.d/rcS
./star_app/led_test

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define MAGIC 'h'
#define LED_ON 		_IOW(MAGIC,1,int)
#define LED_OFF 	_IOW(MAGIC,0,int)

static void my_sleep(int n){
	int j;
	for(j=0; j<10000000*n; j++);
}

int main(void)
{
	printf("-------------------------------\n"
	<span style="white-space:pre">	</span>"||   start:一闪一闪亮晶晶    ||\n"
		"-------------------------------\n");
	my_sleep(1);

	int fd;
	unsigned int cmd=0;
	unsigned long val=0;

	fd=open("/dev/led", O_RDWR);
	if(fd<0){
		perror("open failed!\n");
		exit(1);
	}

	int i;
	for(i=0; i<10; i++){
		if(i%2==0)
			cmd=LED_OFF;
		else
			cmd=LED_ON;

		val=3;            //亮 led3
		if(ioctl(fd,cmd,val)<0){
			perror("ioctl failed!\n");
			exit(1);
		}

		val=4;            //亮 led4
		if(ioctl(fd,cmd,val)<0){
			perror("ioctl failed!\n");
			exit(1);
		}
		my_sleep(1);
	}

	close(fd);
	return 0;
}
时间: 2024-10-22 05:29:36

喜羊羊系列之【设备 - 驱动 编入内核】的相关文章

喜羊羊系列【设备 - 驱动器 编译进内核】

博客:http://blog.csdn.net/muyang_ren 这篇和设备-驱动动态载入进内核做对照 *针对不同的平台.可能放进的不是以下的文件内,仅仅做參考 1.头文件 放进:linux-3.0.8\arch\arm\plat-samsung\include\plat led.h #ifndef _HEAD_H #define _HEAD_H #define MAGIC 'h' #define LED_ON _IOW(MAGIC,1,int) #define LED_OFF _IOW(M

喜羊羊系列之【设备-驱动 动态加载进内核】

博客:http://blog.csdn.net/muyang_ren 这篇和设备驱动编入内核做对比 led.h #ifndef _HEAD_H #define _HEAD_H #define MAGIC 'h' #define LED_ON _IOW(MAGIC,1,int) #define LED_OFF _IOW(MAGIC,0,int) struct led_device{ dev_t devno; unsigned int led_major; struct cdev *led_cdev

Hasen的linux设备驱动开发学习之旅--异步I/O

/** * Author:hasen * 参考 :<linux设备驱动开发详解> * 简介:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:异步I/O * Date:2014-11-11 */ linux中最常用的输入/输出(I/O)模型是同步I/O.在这个模型中,请求发出后,应用就会阻塞,知道请求满足 为止.但是在某些情况下,I/O请求可能需要与其他的进程进行交叠.可移植操作系统接口(POSIX)异步I/O(AIO) 应用程序接口(API)就提供了这种功能. AIO基本

Linux设备驱动核心理论(二)

7.Linux设备驱动中的并发控制 7.1 并发与竞态 并发(concurrency)指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源(硬件资源和软件上的全局变量.静态变量等)的访问则很容易导致竞态(race conditions). 1.对称多处理器(SMP)的多个CPU SMP是一种紧耦合.共享存储的系统模型,其体系结构如下图,它的特点是多个CPU使用共同的系统总线,因此可访问共同的外设和存储器. 2.单CPU内进程与抢占它的进程 Linux 2.6内核支持抢占调度,一个进程内

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获

字符设备驱动编程(一)

当我们对字符设备进行编程的时候,需要做一些常有的准备工作,获取设备号,对设备文件操作函数的注册,文件信息的初始化,文件的内核表现形式,向内核的注册等等. 对字符设备的访问是通过文件系统内的设备名称进行的,通常在/dev目录下.使用ls -l 每行的第一个字符用来识别该文件类型,c就是字符设备驱动文件.b就是块设备驱动文件.内核通过主次设备号来进行管理设备.主设备号表示对应的驱动程序(虽然linux允许多个驱动程序共享主设备号,但是绝大部分的设备还是一个主设备号对应一个驱动程序),次设备号表示具体

字符设备驱动、平台设备驱动、设备驱动模型、sysfs的关系

Linux驱动开发的童鞋们来膜拜吧:-)  学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关联的分析.对于开发者而言,能够熟悉某一点并分享出来已很难得,但对于专注传授技术和经验给

深入浅出~Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

驱动学习之字符设备驱动的原理

1:嵌入式系统的整体工作原理 应用层->API->设备驱动->硬件 比如,在应用层,现在使用read函数去读取一个设备文件,这个read函数是属于应用层的,它不能直接读取设备文件,而是通过内核层的函数(其实就是和file_operations结构体中read这个函数指针相绑定的函数,这个函数才是真正操作硬件的函数)来实现读取文件, 2:file_operations结构体 (1)这个结构体里面存放的是一个驱动里面操作文件的各种函数指针,比如,现有一个驱动,它可以打开一个文件,那么这个驱动