led字符驱动设备
#include <linux/init.h>
#include <linux/module.h> //内核模块的头文件
#include <linux/fs.h> //for MKDEV register_chrdev_region
#include <linux/cdev.h> //字符设备头文件
#include <asm/io.h> //for ioremap
#include <asm/uaccess.h> //for copy from user
#include <linux/sched.h> //for wait queue
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#define LED_MA 520 //主设备号,表示某一类设备
#define LED_MI 0
struct cdev led; //定义字符设备
//------把字符设备改造成平台设备
//1. 把硬件信息剥离出去, 集中放在设备数里
//#define GPX1CON 0X11000C20
//#define GPX1DAT 0X11000C24
unsigned int * led3_con;
unsigned int * led3_dat;
struct mutex lock; //定义互斥体
static wait_queue_head_t queue; //定义等待队列
int glen=0; //模拟初始时设备没有数据
struct fasync_struct *async_queue; //定义异步通知
static DEFINE_SEMAPHORE(semlock); //定义二元信号量
int led_open(struct inode * inode, struct file * file)
{
// mutex_lock(&lock); //上锁
//down(&semlock);
//led3 on 内核驱动不能直接访问硬件层地址
// *(volatile unsigned int *)GPX1CON = 0X01; //set led3 output
// *(volatile unsigned int *)GPX1DAT = 1; //set led3 high
//writel((readl(gpx2con) & ~(0xf << 28)) | (0x1 << 28), gpx2con);
// *(volatile unsigned int *)led3_con = 0X01; //set led3 output
writel((readl(led3_con) & ~(0xf << 0)) | (0x1 << 0),led3_con);//set led3 output
// *(volatile unsigned int *)led3_dat = 1; //set led3 high
writel(readl(led3_dat) | 1,led3_dat);
printk("led open ok\n");
return 0;
}
int led_close (struct inode *inode, struct file *file)
{
//mutex_unlock(&lock); //释放锁
// up(&semlock);
*(volatile unsigned int *)led3_dat = 0; //set led3 low
printk("led_close \n");
return 0;
}
//#define LED3_ON 1
//#define LED3_OFF 2
#define LED_MAGIC 'L'
#define LED_ON _IOW(LED_MAGIC, 1, int)
#define LED_OFF _IOW(LED_MAGIC, 2, int)
long led_ioctl (struct file *file, unsigned int cmd, unsigned long args)
{
switch(cmd){
case LED_ON:
//*(volatile unsigned int *)led3_dat = 1; //set led3 high
writel(readl(led3_dat) | 1,led3_dat);
printk("led o11 cmd=%d\n",cmd);
break;
case LED_OFF:
//*(volatile unsigned int *)led3_dat = 0; //set led3 low
writel(readl(led3_dat) & (~1),led3_dat);
printk("led off\n");
break;
}
return 0;
}
#define BUF_LEN 64
char data_buf[BUF_LEN];
ssize_t led_write (struct file *file, const char __user * buf, size_t count, loff_t *flags)
{
int ret;
/*应用空间的数据不能直接拷贝到内核
while(count--){
*data_buf++ = *buf++;
}
*/
if (count > BUF_LEN -1){
return -ENOMEM;
}
if (count<0) {
return -EINVAL;
}
if(buf==NULL){
printk("user buf is null fail\n");
return -1;
}
//返回值:0表示成功,正值表示拷贝失败的字节数
ret = copy_from_user(data_buf,buf,count); //实现应用空间的数据拷贝到内核层
if(ret){
printk("copy from user fail ret =%d\n",ret);
return -EFAULT;
}
data_buf[BUF_LEN -1]='\0';
printk ("Received: %s\n", data_buf);
ret = count;
glen = count;
wake_up_interruptible(&queue); //唤醒等待队列中等待的进程,依次获取
kill_fasync(&async_queue, SIGIO, POLL_IN); //发送SIGIO 异步通知信号
printk("uart_write \n");
return ret;
}
ssize_t led_read (struct file *filp, char *buf, size_t count, loff_t *offp)
{
ssize_t result = 0;
wait_event_interruptible(queue, glen!=0); //等待(当条件不满足时,则进程会进入等待队列睡眠濉�—澹�
glen =0;
if(count > BUF_LEN -1 ) {
count = BUF_LEN -1;
}
if(count < 0) {
return -EINVAL;
}
if(buf==NULL){
printk("user buf is null fail\n");
return -1;
}
if (copy_to_user(buf,data_buf, count)) {//实现内核层数据拷贝应用层
printk(" copy to user fail\n");
result = -EFAULT;
return result;
}
printk ("read %d bytes\n", count);
result = count;
return result;
}
unsigned int led_poll (struct file *file, struct poll_table_struct * table)
{
int dev_status=0;
printk(" led poll go");
poll_wait(file,&queue,table); //poll的初始化
if(glen > 0){
dev_status |= POLLIN|POLLRDNORM; //用返回值通知上层,设备的状态
}
if(glen <BUF_LEN){
dev_status |= POLLOUT | POLLWRNORM; /*标示数据可写入*/
}
return dev_status;
}
int led_fasync (int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &async_queue); //处理标志的变更
}
//3.实现设备的文件操作
struct file_operations led_fops={
.open = led_open,
.release = led_close,
.unlocked_ioctl = led_ioctl,
.write =led_write,
.read =led_read,
.poll =led_poll,
.fasync = led_fasync,
};
struct resource *res_led3con;
struct resource *res_led3dat;
int led_init(struct platform_device *pdev){
int ret;
dev_t dev_id;
//1.注册设备号
dev_id =MKDEV(LED_MA,LED_MI); //合并主次设备号为设备ID
ret = register_chrdev_region(dev_id,1,"chengdu_led");
if(ret<0){
printk(" register_chrdev_region fail \n");
return -1;
}
//2.初始化字符设备
cdev_init(&led,&led_fops);
ret = cdev_add(&led,dev_id,1);
if(ret<0){
unregister_chrdev_region(dev_id, 1); //取消注册
printk(" cdev_add fail \n");
return -1;
}
res_led3con = platform_get_resource(pdev,IORESOURCE_MEM,0);
if(res_led3con ==NULL){
printk(" platform_get_resource fail \n");
return -1;
}
res_led3dat = platform_get_resource(pdev,IORESOURCE_MEM,1);
if(res_led3dat ==NULL){
printk(" platform_get_resource fail \n");
return -1;
}
//映射硬件地址到内核地址
// led3_con = ioremap(pdev->resource[0].start,1);
led3_con = ioremap(res_led3con->start,1);
if(led3_con ==NULL){
cdev_del(&led);
unregister_chrdev_region(dev_id, 1); //取消注册
printk("led3_con ioremap fail \n");
return -1;
}
// led3_dat = ioremap(pdev->resource[1].start,1);
led3_dat = ioremap(res_led3dat->start,1);
if(led3_dat ==NULL){
printk("led3_dat ioremap fail \n");
return -1;
}
mutex_init(&lock); //初始化互斥体
init_waitqueue_head(&queue); //初始化等待队列
printk("led init go 1\n");
return 0;
}
void led_remove(void){
dev_t dev_id;
dev_id =MKDEV(LED_MA,LED_MI);
cdev_del(&led);
unregister_chrdev_region(dev_id, 1); //取消注册
printk("led_remove go 1\n");
}
//module_init(led_init); //声明模块加载的入口 insmod led.ko
//module_exit(led_remove); //声明模块的卸载入口 rmmod led
const struct of_device_id dev_matches[] = {
{ .compatible = "chengdu,led"}, //必须要和设备树里的一致
{},
};
struct platform_driver led_platform_driver = {
.driver = {
.name = "chenduled",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(dev_matches),
},
.probe = led_init, //当在设备树里找到对应的平台设备,才会调用probe函数
.remove = led_remove,
};
module_platform_driver(led_platform_driver); //平台设备驱动入口 insmod led.ko
MODULE_LICENSE("Dual BSD/GPL"); //免费开源 许可声明