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