Linux设备驱动开发之流水灯

int register_chrdev(unsigned int major, 
					const char *name,                     
                  const struct file_operations *fops)
功能:注册一个字符设备驱动
参数:
	@major :主设备号
		major>0 系统认为这个major就是主设备号
		major=0 系统自动分配一个主设备号
		
	设备号(32位)=主设备号(高12)+次设备号(低20)
	主设备号:它是哪一类设备 LED UART BEEP WDT
	次设备号:同类中的第几个设备
	
	@name :给你的字符设备驱动起的名字
		通过 cat /proc/devices 可以查看
		Character devices:
		  1 		mem
		  4 		/dev/vc/0
		  4 		tty
		  4 		ttyS
		  5 		/dev/tty
		  5 		/dev/console
		  5 		/dev/ptmx
		  5 		ttyprintk
		  6 		lp
		  7 		vcs
		  |          |
		  主设备号  名字
	
	@fops:操作方法结构体
		vi  -t file_operations
		struct file_operations {
			ssize_t (*read) (struct file *,
				char __user *, size_t, loff_t *);
				
			ssize_t (*write) (struct file *, 
				const char __user *, size_t, loff_t *);
			int (*open) (struct inode *, struct file *);
			int (*release) (struct inode *, struct file *);
		}
	
返回值:
	major>0	 成功返回0,失败返回错误码(负数)
	major=0	 成功返回主设备号,失败返回错误码
void unregister_chrdev(unsigned int major, const char *name)    
 功能:注销一个字符设备驱动
 参数:
	@major:主设备号
	@name : 名字
返回值:无

	指定驱动文件的tags就是内核顶层的tags
	:set tags=/home/linux/kernel/kernel-3.4.39/tags
驱动的测试流程
	1.安装驱动
		sudo insmod mycdev.ko
		
	2.查看字符设备驱动是否创建成功
		cat /proc/devices 
		250       mycdev
		|          |
		主设备号  名字
	3.创建设备文件(mknod)
		sudo mknod 路径(任意)/设备文件的名字  c/b  主设备号   次设备号
		sudo mknod /dev/mycdev c 250 0
		
		
	4.编写应用程序
		在应用程序中调用open  read  write  close函数
		
		
	5.执行应用程序
		sudo ./a.out
		或者
		sudo chmod 777 /dev/mycdev
		./a.out
	6.查看
		dmesg
		如果看到驱动中的open read  write close都打印了
		说明,应用程序调用驱动就成功了。
用户空间和内核空间数据传递
	在内核中有asm/uaccess.h或者asm-generic/uaccess.h
	里面写的都是同样类型的函数,如果包含头文件的时候
	写上其中一种,可能在当前的内核版本中编译能通过,
	换一个内核版本编译就通过不了了,把前面的asm换成
	linux/uaccess.h,这是一个链接文件,它总能链接到正确
	的头文件的位置。
	
	#include <linux/uaccess.h>
	
	unsigned long copy_from_user(void *to, 
		const void __user *from, unsigned long n)
	功能:将数据从用户空间拷贝到内核空间
	参数:
		@to  :内核空间的首地址
		@from:用户空间的首地址
		@n   :拷贝的字节的个数
	返回值:
		成功返回0,失败返回未拷贝的字节的个数
		
	内核中错误码问题:
		vi -t EIO;
		
	unsigned long copy_to_user(void __user *to, 
		const void *from, unsigned long n)
	
	功能:将数据从内核空间拷贝到用户空间
	参数:
		@to  :用户空间的首地址
		@from:内核空间的首地址
		@n   :拷贝的字节的个数
	返回值:
		成功返回0,失败返回未拷贝的字节的个数
驱动和硬件的交互过程
	由于驱动运行在3-4G的虚拟地址中,LED灯的
	控制寄存器它是物理地址。所以如果想在内核空间
	操作物理地址,需要将物理地址映射成虚拟地址。
	
	void *ioremap(phys_addr_t offset, unsigned long size)   
	功能:将物理地址映射成虚拟地址
	参数:
		@offset:物理地址
		@size  :映射的长度(字节)
	返回值:成功返回虚拟地址,失败返回NULL


	void iounmap(void *addr)                                          
	功能:取消映射
	参数:
		@addr:虚拟地址
	返回值:无
ioctl使用
	man ioctl
	
    #include <sys/ioctl.h>
    int ioctl(int fd, int request, ...);
	功能:input /output control device
	参数:
		@fd     :打开设备得到的文件描述符
		@request:通过同宏_IO _IOR _IOW  _IOWR封装的请求码
		@...    :可变参数
---------------------------------------------
fops:long (*unlocked_ioctl) (struct file *,
		unsigned int cmd, unsigned long args);	
		参数:
			cmd:就是用户空间传递过来的request
			args:就是用户空间传递过来的...
	

	应用层的ioctl被调用的时候驱动的fops中的
	unlocked_ioctl就会被执行。

	request这个32位请求码,每个部分的含义
	 31-30  00 - no parameters: uses _IO macro
		10 - read: _IOR
		01 - write: _IOW
		11 - read/write: _IOWR

	 29-16  size of arguments

	 15-8   ascii character supposedly
		unique to each driver

	 7-0    function #
                       
	#define _IO(type,nr)		
		_IOC(_IOC_NONE,(type),(nr),0)
		
	#define _IOR(type,nr,size)	
		_IOC(_IOC_READ,(type),(nr),(sizeof(size)))
	#define _IOW(type,nr,size)	
		_IOC(_IOC_WRITE,(type),(nr),(sizeof(size)))
	#define _IOWR(type,nr,size)	
		_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(sizeof(size)))


	#define _IOC(dir,type,nr,size) \
	(((dir)  << _IOC_DIRSHIFT) | \
	 ((type) << _IOC_TYPESHIFT) | \
	 ((nr)   << _IOC_NRSHIFT) | \
	 ((size) << _IOC_SIZESHIFT))


	 2       14        8      8
	 dir     size     type    nr
		
	#define VFAT_IOCTL_READDIR_BOTH        
		_IOR('r', 1, struct dirent [2])
	
	
	#define RED_ON   _IO('a',0)
	#define RED_OFF  _IO('a',1)
自动创建设备节点的过程
user	hotplug ---------------> udev/mdev
		|                       |----->/dev/设备节点名
		|/sys/class/目录/设备名
--------|-------------------------------------
kernel	|
		|
	1.提交目录
	class_create(owner, name)	
	2.提交设备名
	struct device *device_create(struct class *class,
	struct device *parent,dev_t devt, void *drvdata, 
	const char *fmt, ...)

	创建设备节点的函数接口讲解如下:
	1.class_create(owner, name)
	功能:提交目录
	参数:
		@owner :THIS_MODULE
			(以后所有的驱动遇到owner就写THIS_MODULE,
			编译驱动之后会生成一个文件,这个文件通过this_module
			记录程序的入口和出口)
		@name :目录名
		
	返回值:成功返回struct class *的结构体指针,
			失败返回错误码指针(void *)-5;
	
	
	cls = class_create(owner, name);
	思考如何判断这个函数返回的错误?
	
	*((int *)ret) < 0  错误
	sizeof(*ret) == 4; 错误
	
	IS_ERR(cls)
	如果返回值为真,表示他是一个错误,否则不是一个错误。
	
	ERR_PTR(long error)
	将错误码转化为错误码指针
	
	PTR_ERR(const void *ptr)
	将错误码指针转化为错误码
	
	2.struct device *device_create(struct class *class,
	struct device *parent,dev_t devt, void *drvdata, 
	const char *fmt, ...)
	功能:提交设备节点名
	参数:
		@class :cls的结构体指针
		@parent:NULL
		@devt  : 设备号
		@drvdata:NULL
		@fmt   :设备节点的名字
	返回值:成功返回struct device *的结构体指针,
			失败返回错误码指针
	

	MKDEV(ma,mi) //根据主次设备号合成设备号
	MAJOR(dev)   //根据设备号得到主设备号
	MINOR(dev)   //根据设备号得到次设备号
	
	
	void class_destroy(struct class *cls)
	功能:注销class
	
	
	void device_destroy(struct class *class, dev_t devt)
	功能:注销device
myled.c


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

#define CNAME "myled" 
#define RED_PHY_BASE 0xc001a000   //a28
#define BLUE_PHY_BASE 0xc001b000  //b12
#define GREEN_PHY_BASE 0xc001e000 //e13

#define OUT    0
#define OUTENB 1
#define ALTFN0 8
#define ALTFN1 9

int major = 0;
char kbuf[2] = {0};

//映射之后产生的虚拟地址
unsigned int *red_virt_base = NULL;
unsigned int *green_virt_base = NULL;
unsigned int *blue_virt_base = NULL;

enum RGB_LED{
	RED,
	GREEN,
	BLUE,
};

int myled_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_read(struct file *file, 
	char __user *ubuf, size_t size, loff_t *offs)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_write (struct file *file, 
	const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;

	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

	//1.拷贝用户空间的数据
	if(size > sizeof(kbuf)) size = sizeof(kbuf);
	ret = copy_from_user(kbuf,ubuf,size);
	if(ret){
		printk("copy data from user error\n");
		return -EIO;
	}

	//2.根据用户空间的数据进行点灯
	switch(kbuf[0]){
		case   RED: 
			kbuf[1]?(*(red_virt_base + OUT) |= (1<<28)):\
				(*(red_virt_base + OUT ) &= ~(1<<28));
			break;
		case GREEN: 	
			kbuf[1]?(*(green_virt_base + OUT) |= (1<<13)):\
				(*(green_virt_base + OUT) &= ~(1<<13));
			break;
		case  BLUE: 	
			kbuf[1]?(*(blue_virt_base + OUT) |= (1<<12)):\
				(*(blue_virt_base + OUT) &= ~(1<<12));
			break;
	}

	return size;
}

int myled_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = myled_open,
	.read = myled_read,
	.write = myled_write,
	.release = myled_close,
};

static int __init myled_init(void)
{
	//1.注册字符设备驱动
	major = register_chrdev(0,CNAME,&fops);
	if(major < 0){
		printk("register chrdev error\n");
		return major;
	}

	//2.映射RGB_LED灯的地址
	red_virt_base = ioremap(RED_PHY_BASE,40);
	if(red_virt_base == NULL){
		printk("ioremap red led addr error\n");
		return -ENOMEM;
	}
	green_virt_base = ioremap(GREEN_PHY_BASE,40);
	if(green_virt_base == NULL){
		printk("ioremap green led addr error\n");
		return -ENOMEM;
	}
	blue_virt_base = ioremap(BLUE_PHY_BASE,40);
	if(blue_virt_base == NULL){
		printk("ioremap blue led addr error\n");
		return -ENOMEM;
	}

	//3.RGB_LED  INIT ALL OFF
	*(red_virt_base + ALTFN1) &= ~(3<<24);  //altfn1 24:25  gpio
	*(red_virt_base + OUTENB) |= (1<<28);  //outenb 28 输出
	*(red_virt_base + OUT   ) &= ~(1<<28); //out 28 low

	*(blue_virt_base + ALTFN0) &= ~(3<<24); 
	*(blue_virt_base + ALTFN0) |= (2<<24); 
	*(blue_virt_base + OUTENB) |= (1<<12);  
	*(blue_virt_base + OUT   ) &= ~(1<<12); 

	*(green_virt_base + ALTFN0) &= ~(3<<26); 
	*(green_virt_base + OUTENB) |= (1<<13);  
	*(green_virt_base + OUT   ) &= ~(1<<13); 

	
	return 0;
}

static void __exit myled_exit(void)
{
	//1.注销字符设备驱动
	unregister_chrdev(major,CNAME);

	//2.取消映射
	iounmap(red_virt_base);
	iounmap(blue_virt_base);
	iounmap(green_virt_base);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");


test.c

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

char ubuf[2] = {0,0};
int main(int argc, const char *argv[])
{
	int fd;
	fd = open("/dev/myled",O_RDWR);
	if(fd == -1){
		perror("open error");
		return -1; 
	}

	while(1){
		sleep(1);
		
		//ubuf[1] = !ubuf[1];  
		
		ubuf[1] = ubuf[1]?0:1;

		write(fd,ubuf,sizeof(ubuf));
	}

	close(fd);
	return 0;
}


Makefile
MODNAME?=demo
#KERNELDIR:= /lib/modules/$(shell uname -r)/build/
KERNELDIR:= /home/linux/kernel/kernel-3.4.39/
PWD:=$(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=$(MODNAME).o








myled.c

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

#define CNAME "myled" 
#define RED_PHY_BASE 0xc001a000   //a28
#define BLUE_PHY_BASE 0xc001b000  //b12
#define GREEN_PHY_BASE 0xc001e000 //e13

#define OUT    0
#define OUTENB 1
#define ALTFN0 8
#define ALTFN1 9

int major = 0;
char kbuf[2] = {0};

//映射之后产生的虚拟地址
unsigned int *red_virt_base = NULL;
unsigned int *green_virt_base = NULL;
unsigned int *blue_virt_base = NULL;

enum RGB_LED{
	RED,
	GREEN,
	BLUE,
};

int myled_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_read(struct file *file, 
	char __user *ubuf, size_t size, loff_t *offs)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_write (struct file *file, 
	const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;

	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

	//1.拷贝用户空间的数据
	if(size > sizeof(kbuf)) size = sizeof(kbuf);
	ret = copy_from_user(kbuf,ubuf,size);
	if(ret){
		printk("copy data from user error\n");
		return -EIO;
	}
	//kbuf[2] = {which,status};
	//2.根据用户空间的数据进行点灯
	switch(kbuf[0]){
		case   RED: 
			kbuf[1]?(*(red_virt_base + OUT) |= (1<<28)):\
				(*(red_virt_base + OUT) &= ~(1<<28));
			break;
		case GREEN: 	
			kbuf[1]?(*(green_virt_base + OUT) |= (1<<13)):\
				(*(green_virt_base + OUT) &= ~(1<<13));
			break;
		case  BLUE: 	
			kbuf[1]?(*(blue_virt_base + OUT) |= (1<<12)):\
				(*(blue_virt_base + OUT) &= ~(1<<12));
			break;
	}

	return size;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	switch(cmd){
		case RED_ON:
			*(red_virt_base + OUT) |= (1<<28);
			break;
		case RED_OFF:
			*(red_virt_base + OUT) &= ~(1<<28);
			break;
	}
	return 0;
}

int myled_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = myled_open,
	.read = myled_read,
	.write = myled_write,
	.unlocked_ioctl = myled_ioctl,
	.release = myled_close,
};

static int __init myled_init(void)
{
	//1.注册字符设备驱动
	major = register_chrdev(0,CNAME,&fops);
	if(major < 0){
		printk("register chrdev error\n");
		return major;
	}

	//2.映射RGB_LED灯的地址
	red_virt_base = ioremap(RED_PHY_BASE,40);
	if(red_virt_base == NULL){
		printk("ioremap red led addr error\n");
		return -ENOMEM;
	}
	green_virt_base = ioremap(GREEN_PHY_BASE,40);
	if(green_virt_base == NULL){
		printk("ioremap green led addr error\n");
		return -ENOMEM;
	}
	blue_virt_base = ioremap(BLUE_PHY_BASE,40);
	if(blue_virt_base == NULL){
		printk("ioremap blue led addr error\n");
		return -ENOMEM;
	}

	//3.RGB_LED  INIT ALL OFF
	*(red_virt_base + ALTFN1) &= ~(3<<24);  //altfn1 24:25  gpio
	*(red_virt_base + OUTENB) |= (1<<28);  //outenb 28 输出
	*(red_virt_base + OUT   ) &= ~(1<<28); //out 28 low

	*(blue_virt_base + ALTFN0) &= ~(3<<24); 
	*(blue_virt_base + ALTFN0) |= (2<<24); 
	*(blue_virt_base + OUTENB) |= (1<<12);  
	*(blue_virt_base + OUT   ) &= ~(1<<12); 

	*(green_virt_base + ALTFN0) &= ~(3<<26); 
	*(green_virt_base + OUTENB) |= (1<<13);  
	*(green_virt_base + OUT   ) &= ~(1<<13); 

	return 0;
}

static void __exit myled_exit(void)
{
	//1.注销字符设备驱动
	unregister_chrdev(major,CNAME);

	//2.取消映射
	iounmap(red_virt_base);
	iounmap(blue_virt_base);
	iounmap(green_virt_base);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");





test.c

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

#include "cmd.h"

int main(int argc, const char *argv[])
{
	int fd;
	fd = open("/dev/myled",O_RDWR);
	if(fd == -1){
		perror("open error");
		return -1; 
	}

	while(1){
		ioctl(fd,RED_ON);	
		sleep(1);
		ioctl(fd,RED_OFF);	
		sleep(1);
	}

	close(fd);
	return 0;
}


cmd.h

#ifndef __CMD_H__
#define __CMD_H__

#define RED_ON  _IO('a',0)
#define RED_OFF _IO('a',1)



#endif






Makefile


MODNAME?=demo
#KERNELDIR:= /lib/modules/$(shell uname -r)/build/
KERNELDIR:= /home/linux/kernel/kernel-3.4.39/
PWD:=$(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=$(MODNAME).o



myled.c

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

#define CNAME "myled" 
#define RED_PHY_BASE 0xc001a000   //a28
#define BLUE_PHY_BASE 0xc001b000  //b12
#define GREEN_PHY_BASE 0xc001e000 //e13

#define OUT    0
#define OUTENB 1
#define ALTFN0 8
#define ALTFN1 9

int major = 0;
char kbuf[2] = {0};
int data = 0;
//映射之后产生的虚拟地址
unsigned int *red_virt_base = NULL;
unsigned int *green_virt_base = NULL;
unsigned int *blue_virt_base = NULL;

enum RGB_LED{
	RED,
	GREEN,
	BLUE,
};

int myled_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_read(struct file *file, 
	char __user *ubuf, size_t size, loff_t *offs)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_write (struct file *file, 
	const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;

	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

	//1.拷贝用户空间的数据
	if(size > sizeof(kbuf)) size = sizeof(kbuf);
	ret = copy_from_user(kbuf,ubuf,size);
	if(ret){
		printk("copy data from user error\n");
		return -EIO;
	}
	//kbuf[2] = {which,status};
	//2.根据用户空间的数据进行点灯
	switch(kbuf[0]){
		case   RED: 
			kbuf[1]?(*(red_virt_base + OUT) |= (1<<28)):\
				(*(red_virt_base + OUT) &= ~(1<<28));
			break;
		case GREEN: 	
			kbuf[1]?(*(green_virt_base + OUT) |= (1<<13)):\
				(*(green_virt_base + OUT) &= ~(1<<13));
			break;
		case  BLUE: 	
			kbuf[1]?(*(blue_virt_base + OUT) |= (1<<12)):\
				(*(blue_virt_base + OUT) &= ~(1<<12));
			break;
	}

	return size;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	switch(cmd){
		case RED_ON:
			*(red_virt_base + OUT) |= (1<<28);
			break;
		case RED_OFF:
			*(red_virt_base + OUT) &= ~(1<<28);
			break;
		case ACCESS_DATA_R:
			ret = copy_to_user((void *)args,&data,4);
			if(ret){
				printk("ioctl:copy data to user error\n");
				return -EINVAL;
			}

			break;
		case ACCESS_DATA_W:
			ret = copy_from_user(&data,(void *)args,4);
			if(ret){
				printk("ioctl:copy data from user error\n");
				return -EINVAL;
			}
			break;	
	}

	return 0;
}

int myled_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = myled_open,
	.read = myled_read,
	.write = myled_write,
	.unlocked_ioctl = myled_ioctl,
	.release = myled_close,
};

static int __init myled_init(void)
{
	//1.注册字符设备驱动
	major = register_chrdev(0,CNAME,&fops);
	if(major < 0){
		printk("register chrdev error\n");
		return major;
	}

	//2.映射RGB_LED灯的地址
	red_virt_base = ioremap(RED_PHY_BASE,40);
	if(red_virt_base == NULL){
		printk("ioremap red led addr error\n");
		return -ENOMEM;
	}
	green_virt_base = ioremap(GREEN_PHY_BASE,40);
	if(green_virt_base == NULL){
		printk("ioremap green led addr error\n");
		return -ENOMEM;
	}
	blue_virt_base = ioremap(BLUE_PHY_BASE,40);
	if(blue_virt_base == NULL){
		printk("ioremap blue led addr error\n");
		return -ENOMEM;
	}

	//3.RGB_LED  INIT ALL OFF
	*(red_virt_base + ALTFN1) &= ~(3<<24);  //altfn1 24:25  gpio
	*(red_virt_base + OUTENB) |= (1<<28);  //outenb 28 输出
	*(red_virt_base + OUT   ) &= ~(1<<28); //out 28 low

	*(blue_virt_base + ALTFN0) &= ~(3<<24); 
	*(blue_virt_base + ALTFN0) |= (2<<24); 
	*(blue_virt_base + OUTENB) |= (1<<12);  
	*(blue_virt_base + OUT   ) &= ~(1<<12); 

	*(green_virt_base + ALTFN0) &= ~(3<<26); 
	*(green_virt_base + OUTENB) |= (1<<13);  
	*(green_virt_base + OUT   ) &= ~(1<<13); 

	return 0;
}

static void __exit myled_exit(void)
{
	//1.注销字符设备驱动
	unregister_chrdev(major,CNAME);

	//2.取消映射
	iounmap(red_virt_base);
	iounmap(blue_virt_base);
	iounmap(green_virt_base);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");


test.c


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

#include "cmd.h"

int main(int argc, const char *argv[])
{
	int fd;
	int data = 50;
	fd = open("/dev/myled",O_RDWR);
	if(fd == -1){
		perror("open error");
		return -1; 
	}

	while(1){
		ioctl(fd,RED_ON);	
		sleep(1);
		ioctl(fd,RED_OFF);	
		sleep(1);
		ioctl(fd,ACCESS_DATA_W,&data);
		sleep(1);
		data = 0;
		ioctl(fd,ACCESS_DATA_R,&data);
		printf("data = %d\n",data);
	}

	close(fd);
	return 0;
}


cmd.h

#ifndef __CMD_H__
#define __CMD_H__

#define RED_ON  _IO('a',0)
#define RED_OFF _IO('a',1)

#define ACCESS_DATA_R _IOR('a',0,int)
#define ACCESS_DATA_W _IOW('a',0,int)

#endif





Makefile



MODNAME?=myled
#KERNELDIR:= /lib/modules/$(shell uname -r)/build/
KERNELDIR:= /home/linux/kernel/kernel-3.4.39/
PWD:=$(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc test.c -o APP-myled
	 cp *.ko APP-myled ~/rootfs/
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=$(MODNAME).o





myled.c

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

#define CNAME "myled" 
#define RED_PHY_BASE 0xc001a000   //a28
#define BLUE_PHY_BASE 0xc001b000  //b12
#define GREEN_PHY_BASE 0xc001e000 //e13

#define OUT    0
#define OUTENB 1
#define ALTFN0 8
#define ALTFN1 9

int major = 0;
char kbuf[2] = {0};
int data = 0;
char sbuf[50] = {0};
//映射之后产生的虚拟地址
unsigned int *red_virt_base = NULL;
unsigned int *green_virt_base = NULL;
unsigned int *blue_virt_base = NULL;

enum RGB_LED{
	RED,
	GREEN,
	BLUE,
};

int myled_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_read(struct file *file, 
	char __user *ubuf, size_t size, loff_t *offs)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_write (struct file *file, 
	const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;

	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

	//1.拷贝用户空间的数据
	if(size > sizeof(kbuf)) size = sizeof(kbuf);
	ret = copy_from_user(kbuf,ubuf,size);
	if(ret){
		printk("copy data from user error\n");
		return -EIO;
	}
	//kbuf[2] = {which,status};
	//2.根据用户空间的数据进行点灯
	switch(kbuf[0]){
		case   RED: 
			kbuf[1]?(*(red_virt_base + OUT) |= (1<<28)):\
				(*(red_virt_base + OUT) &= ~(1<<28));
			break;
		case GREEN: 	
			kbuf[1]?(*(green_virt_base + OUT) |= (1<<13)):\
				(*(green_virt_base + OUT) &= ~(1<<13));
			break;
		case  BLUE: 	
			kbuf[1]?(*(blue_virt_base + OUT) |= (1<<12)):\
				(*(blue_virt_base + OUT) &= ~(1<<12));
			break;
	}

	return size;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	switch(cmd){
		case RED_ON:
			*(red_virt_base + OUT) |= (1<<28);
			break;
		case RED_OFF:
			*(red_virt_base + OUT) &= ~(1<<28);
			break;
		case ACCESS_DATA_R:
			ret = copy_to_user((void *)args,&data,4);
			if(ret){
				printk("ioctl:copy data to user error\n");
				return -EINVAL;
			}

			break;
		case ACCESS_DATA_W:
			ret = copy_from_user(&data,(void *)args,4);
			if(ret){
				printk("ioctl:copy data from user error\n");
				return -EINVAL;
			}
			break;	
		case ACCESS_STRING_W:
			printk("size = %d\n",(ACCESS_STRING_W>>16)&0x3fff);
			ret = copy_from_user(sbuf,(void *)args,(ACCESS_STRING_W>>16)&0x3fff);
			if(ret){
				printk("ioctl:copy string from user error\n");
				return -EINVAL;
			}
			break;
		case ACCESS_STRING_R:
			ret = copy_to_user((void *)args,sbuf,(ACCESS_STRING_W>>16)&0x3fff);
			if(ret){
				printk("ioctl:copy string to user error\n");
				return -EINVAL;
			}
			break;
			
	}

	return 0;
}

int myled_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = myled_open,
	.read = myled_read,
	.write = myled_write,
	.unlocked_ioctl = myled_ioctl,
	.release = myled_close,
};

static int __init myled_init(void)
{
	//1.注册字符设备驱动
	major = register_chrdev(0,CNAME,&fops);
	if(major < 0){
		printk("register chrdev error\n");
		return major;
	}

	//2.映射RGB_LED灯的地址
	red_virt_base = ioremap(RED_PHY_BASE,40);
	if(red_virt_base == NULL){
		printk("ioremap red led addr error\n");
		return -ENOMEM;
	}
	green_virt_base = ioremap(GREEN_PHY_BASE,40);
	if(green_virt_base == NULL){
		printk("ioremap green led addr error\n");
		return -ENOMEM;
	}
	blue_virt_base = ioremap(BLUE_PHY_BASE,40);
	if(blue_virt_base == NULL){
		printk("ioremap blue led addr error\n");
		return -ENOMEM;
	}

	//3.RGB_LED  INIT ALL OFF
	*(red_virt_base + ALTFN1) &= ~(3<<24);  //altfn1 24:25  gpio
	*(red_virt_base + OUTENB) |= (1<<28);  //outenb 28 输出
	*(red_virt_base + OUT   ) &= ~(1<<28); //out 28 low

	*(blue_virt_base + ALTFN0) &= ~(3<<24); 
	*(blue_virt_base + ALTFN0) |= (2<<24); 
	*(blue_virt_base + OUTENB) |= (1<<12);  
	*(blue_virt_base + OUT   ) &= ~(1<<12); 

	*(green_virt_base + ALTFN0) &= ~(3<<26); 
	*(green_virt_base + OUTENB) |= (1<<13);  
	*(green_virt_base + OUT   ) &= ~(1<<13); 

	return 0;
}

static void __exit myled_exit(void)
{
	//1.注销字符设备驱动
	unregister_chrdev(major,CNAME);

	//2.取消映射
	iounmap(red_virt_base);
	iounmap(blue_virt_base);
	iounmap(green_virt_base);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");





test.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include "cmd.h"
char ubuf[50] = "hello everyone.............";
int main(int argc, const char *argv[])
{
	int fd;
	int data = 50;
	fd = open("/dev/myled",O_RDWR);
	if(fd == -1){
		perror("open error");
		return -1; 
	}

	while(1){
		ioctl(fd,RED_ON);	
		sleep(1);
		ioctl(fd,RED_OFF);	
		sleep(1);
		ioctl(fd,ACCESS_DATA_W,&data);
		sleep(1);
		data = 0;
		ioctl(fd,ACCESS_DATA_R,&data);
		printf("data = %d\n",data);

		ioctl(fd,ACCESS_STRING_W,ubuf);
		memset(ubuf,0,sizeof(ubuf));
		ioctl(fd,ACCESS_STRING_R,ubuf);
		printf("ubuf = %s\n",ubuf);

	}

	close(fd);
	return 0;
}





cmd.h

#ifndef __CMD_H__
#define __CMD_H__

#define RED_ON  _IO('a',0)
#define RED_OFF _IO('a',1)

#define ACCESS_DATA_R _IOR('a',0,int)
#define ACCESS_DATA_W _IOW('a',0,int)

#define ACCESS_STRING_W _IOW('a',0,char [50])
#define ACCESS_STRING_R _IOR('a',0,char [50])



#endif



Makefile


MODNAME?=myled
#KERNELDIR:= /lib/modules/$(shell uname -r)/build/
KERNELDIR:= /home/linux/kernel/kernel-3.4.39/
PWD:=$(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc test.c -o APP-myled
	 cp *.ko APP-myled ~/rootfs/
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=$(MODNAME).o



myled.c

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

#define CNAME "myled" 
#define RED_PHY_BASE 0xc001a000   //a28
#define BLUE_PHY_BASE 0xc001b000  //b12
#define GREEN_PHY_BASE 0xc001e000 //e13

#define OUT    0
#define OUTENB 1
#define ALTFN0 8
#define ALTFN1 9

int major = 0;
char kbuf[2] = {0};
int data = 0;
char sbuf[50] = {0};
//映射之后产生的虚拟地址
unsigned int *red_virt_base = NULL;
unsigned int *green_virt_base = NULL;
unsigned int *blue_virt_base = NULL;
struct class *cls = NULL;
struct device *dev = NULL;

enum RGB_LED{
	RED,
	GREEN,
	BLUE,
};

int myled_open(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_read(struct file *file, 
	char __user *ubuf, size_t size, loff_t *offs)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

ssize_t myled_write (struct file *file, 
	const char __user *ubuf, size_t size, loff_t *offs)
{
	int ret;

	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

	//1.拷贝用户空间的数据
	if(size > sizeof(kbuf)) size = sizeof(kbuf);
	ret = copy_from_user(kbuf,ubuf,size);
	if(ret){
		printk("copy data from user error\n");
		return -EIO;
	}
	//kbuf[2] = {which,status};
	//2.根据用户空间的数据进行点灯
	switch(kbuf[0]){
		case   RED: 
			kbuf[1]?(*(red_virt_base + OUT) |= (1<<28)):\
				(*(red_virt_base + OUT) &= ~(1<<28));
			break;
		case GREEN: 	
			kbuf[1]?(*(green_virt_base + OUT) |= (1<<13)):\
				(*(green_virt_base + OUT) &= ~(1<<13));
			break;
		case  BLUE: 	
			kbuf[1]?(*(blue_virt_base + OUT) |= (1<<12)):\
				(*(blue_virt_base + OUT) &= ~(1<<12));
			break;
	}

	return size;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	switch(cmd){
		case RED_ON:
			*(red_virt_base + OUT) |= (1<<28);
			break;
		case RED_OFF:
			*(red_virt_base + OUT) &= ~(1<<28);
			break;
		case ACCESS_DATA_R:
			ret = copy_to_user((void *)args,&data,4);
			if(ret){
				printk("ioctl:copy data to user error\n");
				return -EINVAL;
			}

			break;
		case ACCESS_DATA_W:
			ret = copy_from_user(&data,(void *)args,4);
			if(ret){
				printk("ioctl:copy data from user error\n");
				return -EINVAL;
			}
			break;	
		case ACCESS_STRING_W:
			printk("size = %d\n",(ACCESS_STRING_W>>16)&0x3fff);
			ret = copy_from_user(sbuf,(void *)args,(ACCESS_STRING_W>>16)&0x3fff);
			if(ret){
				printk("ioctl:copy string from user error\n");
				return -EINVAL;
			}
			break;
		case ACCESS_STRING_R:
			ret = copy_to_user((void *)args,sbuf,(ACCESS_STRING_R>>16)&0x3fff);
			if(ret){
				printk("ioctl:copy string to user error\n");
				return -EINVAL;
			}
			break;
			
	}

	return 0;
}

int myled_close(struct inode *inode, struct file *file)
{
	printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
	return 0;
}

const struct file_operations fops = {
	.open = myled_open,
	.read = myled_read,
	.write = myled_write,
	.unlocked_ioctl = myled_ioctl,
	.release = myled_close,
};

static int __init myled_init(void)
{
	//1.注册字符设备驱动
	major = register_chrdev(0,CNAME,&fops);
	if(major < 0){
		printk("register chrdev error\n");
		return major;
	}

	//2.映射RGB_LED灯的地址
	red_virt_base = ioremap(RED_PHY_BASE,40);
	if(red_virt_base == NULL){
		printk("ioremap red led addr error\n");
		return -ENOMEM;
	}
	green_virt_base = ioremap(GREEN_PHY_BASE,40);
	if(green_virt_base == NULL){
		printk("ioremap green led addr error\n");
		return -ENOMEM;
	}
	blue_virt_base = ioremap(BLUE_PHY_BASE,40);
	if(blue_virt_base == NULL){
		printk("ioremap blue led addr error\n");
		return -ENOMEM;
	}

	//3.RGB_LED  INIT ALL OFF
	*(red_virt_base + ALTFN1) &= ~(3<<24);  //altfn1 24:25  gpio
	*(red_virt_base + OUTENB) |= (1<<28);  //outenb 28 输出
	*(red_virt_base + OUT   ) &= ~(1<<28); //out 28 low

	*(blue_virt_base + ALTFN0) &= ~(3<<24); 
	*(blue_virt_base + ALTFN0) |= (2<<24); 
	*(blue_virt_base + OUTENB) |= (1<<12);  
	*(blue_virt_base + OUT   ) &= ~(1<<12); 

	*(green_virt_base + ALTFN0) &= ~(3<<26); 
	*(green_virt_base + OUTENB) |= (1<<13);  
	*(green_virt_base + OUT   ) &= ~(1<<13); 


	//4.创建设备节点
	cls = class_create(THIS_MODULE,"aaaa");
	if(IS_ERR(cls)){
		printk("class create error\n");
		return PTR_ERR(cls);
	}
	//major<<20|0  =设备号
	//设备号=主设备号(12) +次设备号(20)
	//MKDEV(ma,mi) //通过主设备和次设备号合成设备号
	dev =  device_create(cls,NULL,MKDEV(major,0),NULL,"myled");
	if(IS_ERR(dev)){
		printk("class device error\n");
		return PTR_ERR(dev);
	}
	
	return 0;
}

static void __exit myled_exit(void)
{
	//1.注销设备节点
	device_destroy(cls,MKDEV(major,0));
	class_destroy(cls);
	
	//2.取消映射
	iounmap(red_virt_base);
	iounmap(blue_virt_base);
	iounmap(green_virt_base);
	
	//3.注销字符设备驱动
	unregister_chrdev(major,CNAME);

}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");





test.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include "cmd.h"
char ubuf[50] = "hello everyone.............";
int main(int argc, const char *argv[])
{
	int fd;
	int data = 50;
	fd = open("/dev/myled",O_RDWR);
	if(fd == -1){
		perror("open error");
		return -1; 
	}

	while(1){
		ioctl(fd,RED_ON);	
		sleep(1);
		ioctl(fd,RED_OFF);	
		sleep(1);
		ioctl(fd,ACCESS_DATA_W,&data);
		sleep(1);
		data = 0;
		ioctl(fd,ACCESS_DATA_R,&data);
		printf("data = %d\n",data);

		ioctl(fd,ACCESS_STRING_W,ubuf);
		memset(ubuf,0,sizeof(ubuf));
		ioctl(fd,ACCESS_STRING_R,ubuf);
		printf("ubuf = %s\n",ubuf);

	}

	close(fd);
	return 0;
}




cmd.h

#ifndef __CMD_H__
#define __CMD_H__

#define RED_ON  _IO('a',0)
#define RED_OFF _IO('a',1)

#define ACCESS_DATA_R _IOR('a',0,int)
#define ACCESS_DATA_W _IOW('a',0,int)

#define ACCESS_STRING_W _IOW('a',0,char [50])
#define ACCESS_STRING_R _IOR('a',0,char [50])



#endif






Makefile



MODNAME?=myled
#KERNELDIR:= /lib/modules/$(shell uname -r)/build/
KERNELDIR:= /home/linux/kernel/kernel-3.4.39/
PWD:=$(shell pwd)

all:
	make -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc test.c -o APP-myled
	 cp *.ko APP-myled ~/rootfs/
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=$(MODNAME).o



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值