#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/export.h>
#include<linux/types.h>
#define GPM4CON 0x110002E0
volatile unsigned long *baseaddr = 0;
//地址必须要 volatile 修饰,volatile 是防止优化,就是避免某次计算中间值存放在寄存器中没有存入flash,这会导致以后读取的值不是想要的
#define rGPM4CON (*((volatile unsigned long *)(baseaddr + 0)))
#define rGPM4DAT (*((volatile unsigned long *)(baseaddr + 1)))
#define MYLED_MAJOR 0
#define MYLED_NAME "led"
#define MYLED_DRVNUM 1
#define MYLED_NUM 4
dev_t myled_devt; //设备号
int myled_major;
struct class *myled_class; //设备结构体
struct cdev *myled; //设备驱动核心结构
char led_status[MYLED_NUM]={-1,-1,-1,-1};
loff_t led_lseek (struct file *fp, loff_t off, int whence)
{
loff_t newoff=0;
switch(whence)
{
case SEEK_SET:
newoff=off;
break;
case SEEK_CUR:
newoff = fp->f_pos+off;
break;
case SEEK_END:
newoff = 4 + off;
default:
return -EINVAL;
}
if(newoff<0)
newoff = 0;
else if(newoff>4)
newoff = 4;
fp->f_pos=newoff;
return newoff;
}
ssize_t led_read (struct file *fp, char __user *buf, size_t size, loff_t *off)
{
int i=0;
unsigned long ret;
if(*off>4)
return -EINVAL;
if(*off+size>4)
size = 4-*off;
for(i=0;i<MYLED_NUM;i++)
led_status[i]= !(rGPM4DAT&(0x1<<i));
ret = copy_to_user(buf,&led_status[*off],size);
//copy_to_user 还是 copy_from_user 都是到前一个参数,且使用的都是地址
*off = *off+size;
printk("led_read is called\n");
return 0;
}
ssize_t led_write (struct file *fp, const char __user *buf, size_t size, loff_t *off)
{
int i;
unsigned long temp;
if(*off>4)
return -EINVAL;
if(*off + size >4)
size = 4 - *off;
temp = copy_from_user(&led_status[*off],buf,size);
for(i=0;i<MYLED_NUM;i++)
{
if(led_status[i] == 0)
rGPM4DAT |= (0x1<<i);
else if(led_status[i] == 1)
rGPM4DAT &= ~(0x1<<i);
else
return -EINVAL;
}
*off = *off + size;
printk("led_write is called \n");
return 0;
}
int led_open (struct inode *node, struct file *fp)
{
int i=0;
fp->private_data =(void *) MINOR(node->i_rdev);
rGPM4CON &= ~(0xffff);
rGPM4CON |= (0x1111);
for(i=0;i<MYLED_NUM;i++)
{
led_status[i] = !(rGPM4DAT&(0x1<<i));
}
printk("led_open is called \n");
return 0;
}
int led_close (struct inode *node, struct file *fp)
{
printk("led_close is called \n");
return 0;
}
struct file_operations led_fops=
{
.owner=THIS_MODULE,
.open =led_open,
.release = led_close,
.read=led_read,
.write = led_write,
.llseek = led_lseek,
};
static int __init myled_init(void) //形参不能为空
{
int i=0;
#if MYLED_MAJOR //如果不为 0,则设备号就是自主分配
myled_major = MYLED_MAJOR;
myled_devt = MKDEV(myled_major,0);
if(register_chrdev_region(myled_devt,MYLED_DRVNUM,MYLED_NAME)) //静态注册设备号
#else
if(alloc_chrdev_region(&myled_devt,0,MYLED_DRVNUM,MYLED_NAME)) //主设备号为 0 ,动态注册设备号
#endif
{
printk("注册失败\n");
return -EBUSY;
}
myled = cdev_alloc(); //分配设备空间
/*
这里多说两句,每一个设备都是一个结构体,这个结构体包括
struct kobject kobj; の,忘了干嘛的
struct module *owner; 所属的模块
const struct file_operations *ops; 所使用的文件操作函数
struct list_head list; 链表,向内和联表添加
dev_t dev; 设备号
unsigned int count; 设备数目
*/
cdev_init(myled,&led_fops); //初始化设备
/*
这个初始化这是把文件函数结构体添加到设备结构体
*/
if(cdev_add(myled,myled_devt,MYLED_DRVNUM)) //添加设备空间
{
printk("注册成功\n");
return -EBUSY;
}
printk("注册成功\n");
myled_class = class_create(THIS_MODULE,MYLED_NAME); //产生一个设备类
for(i=0;i<MYLED_DRVNUM;i++) //添加设备类进内核
device_create(myled_class,NULL,myled_devt+i,NULL,"myled%d",i);
baseaddr = ioremap(GPM4CON, 8); //硬件地址映射到虚拟地址
return 0;
}
static void __exit myled_exit(void) //形参不能为空,必须有个void
{
iounmap(baseaddr); //取消地址映射
device_destroy(myled_class, myled_devt); //销毁在内核的设备
class_destroy(myled_class); //销毁设备类
cdev_del(myled); //删除设备
unregister_chrdev_region(myled_devt,MYLED_DRVNUM); //注销设备
printk("注销成功\n");
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
一个LED驱动的编写
最新推荐文章于 2024-08-09 12:39:11 发布