1.主设备号:表示某类设备
2.次设备号:表示某个设备
文件描述符fd
- 通过文件描述符的下标得到文件指针,通过文件指针的地址,访问文件信息得到(文件标志,当前位置偏移量,v节点指针),调用一次则占用一个文件下标即一个文件信息
驱动函数
C库: fopen fclose fread fwrite fseek ftell
系统
调用 open close read write lseek lseek
驱动
程序 open release read write llseek llseek(驱动程序里完成核心操作,辅助操作系统代替完成)
驱动函数的代码
/*======================================================================
A globalmem driver as an example of char device drivers
The initial developer of the original code is Hiro Wang
<author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#define GLOBALMEM_SIZE 0x1000 /*全局内存最大4K字节*/
#define MEM_CLEAR 0x1 /*清0全局内存*/
#define GLOBALMEM_MAJOR 288 /*预设的globalmem的主设备号*/
static int globalmem_major = GLOBALMEM_MAJOR;
/*globalmem设备结构体*/
struct globalmem_dev
{
struct cdev cdev; /*cdev结构体*/
unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/
};
struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalmem_open(struct inode *inode, struct file *filp)//参数是系统调用open()函数时,内核空间执行的辅助代码传给驱动函数open
{
/*将设备结构体指针赋值给文件私有数据指针*/
filp->private_data = globalmem_devp;
return 0;
}
/*文件释放函数*/
int globalmem_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctl设备控制函数 */
/* old version
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned
int cmd, unsigned long arg)*/
//可以去操作状态寄存器与控制寄存器(read,write只能操作数据寄存器,想要得到状态,或者修改运行状态只能用ioctl驱动函数)
static long globalmem_ioctl(struct file *filp, unsigned
int cmd, unsigned long arg)
{
struct globalmem_dev *dev = filp->private_data;/*获得设备结构体指针*/
switch (cmd)//请求做什么
{
case MEM_CLEAR://对那个空间做清空操作
memset(dev->mem, 0, GLOBALMEM_SIZE);
filp->f_pos = 0;
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
/*读函数*/
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)//第一个参数:文件描述符,第二个参数:buf里面保存的是用户空间的地址,第三个参数:读size个字节,第四个参数:当前位置
{
unsigned long p = *ppos;//当前位置,在文件里多少字节
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= GLOBALMEM_SIZE)//表示p指向的全局内存外面去了,越界,即是异常判断
return count ? - ENXIO: 0;
if (count > GLOBALMEM_SIZE - p)//期望读的字节数大与还剩下的字节数
count = GLOBALMEM_SIZE - p;//
/*内核空间->用户空间*/
if (copy_to_user(buf, (void*)(dev->mem + p), count))//从p指向的当前位置开始读内核空间地址的count字节的内容读用户空间
{
ret = - EFAULT;//失败,返回一个非法值
}
else
{
*ppos += count;//
ret = count;//
printk(KERN_INFO "read %d bytes(s) from %d\n", count, (int)p);
}
return ret;
}
/*写函数*/
static ssize_t globalmem_write(struct file *filp, const char __user *buf,
size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= GLOBALMEM_SIZE)
return count ? - ENXIO: 0;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p;
/*用户空间->内核空间*/
if (copy_from_user(dev->mem + p, buf, count))//把用户空间里的内容count个字节写到内核空间里去
ret = - EFAULT;
else
{
*ppos += count;//当前位置指示器要变大
ret = count;//实际写的字节数
printk(KERN_INFO "written %d bytes(s) from %d\n", count, (int)p);
}
return ret;
}
/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{//orig:
loff_t ret = 0;
switch (orig)
{
//文件头开始偏移
case 0: /*相对文件开始位置偏移*/
if (offset < 0)
{
ret = - EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE)
{
ret = - EINVAL;
break;
}
//当前位置偏移量等于要偏移的字节数
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;//返回实际偏移的数
break;
case 1: /*相对文件当前位置偏移*/
if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
{
ret = - EINVAL;
break;
}
if ((filp->f_pos + offset) < 0)
{
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
case 2: /*相对文件尾位置偏移*/
if (offset > 0)
{
ret = - EINVAL;
break;
}
if ((0-offset) > GLOBALMEM_SIZE)
{
ret = - EINVAL;
break;
}
filp->f_pos = GLOBALMEM_SIZE + offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret;
}
/*文件操作结构体*/
static const struct file_operations globalmem_fops =
{//const表示只读变量
//以下是内核驱动函数
.owner = THIS_MODULE,//固定填THIS
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.unlocked_ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,//相当于close
};
/*初始化并注册cdev*/
//传进来一个指针和此设备号
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
int err, devno = MKDEV(globalmem_major, index);//将设备加入内核管理
//cdev_init第一个传的结构体第一个成员的地址
//第二个是一个结构体类型变量的初始化
//有了结构体变量的首地址,就能查看别的所有的成员内容
cdev_init(&dev->cdev, &globalmem_fops);//将fops里的对象的指针赋值给dev_cdev,open之后,找到与打开的设备号相同的设备
dev->cdev.owner = THIS_MODULE;
// dev->cdev.ops = &globalmem_fops;
err = cdev_add(&dev->cdev, devno, 1);//添加到字符设备链表里面
if (err)
printk(KERN_NOTICE "Error %d when adding cdev %d", err, index);
}
/*设备驱动模块加载函数*/
static int __init globalmem_init(void)
{
int result = -1;
dev_t devno = MKDEV(globalmem_major, 0);//globalmem_major:主设备号,0:次设备号,dev_t:整型
/* 申请设备号*/
if (globalmem_major)//向内核提问,这个设备号是否已经被占用了,如果占用了就失败了,没有占用我就占用成功
{
result = register_chrdev_region(devno, 1, "globalmem");//在第一个目录下有一个设备文件,名字为globalmem
}
if(result < 0) /* 动态申请设备号 */
{//占用了,就向内核提问,有没有空的主设备号
result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
globalmem_major = MAJOR(devno);//将主设备号放到globalmem_major中
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
globalmem_devp = (struct globalmem_dev *)kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);//kmalloc内核不能用C库的malloc,只能用kmalloc,需要空间只是一小块是就用GFP_KERNEL
if (!globalmem_devp) /*申请失败*/
{
result = - ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp, 0, sizeof(struct globalmem_dev));//空间里是不确定的内容,所以要memset清除
globalmem_setup_cdev(globalmem_devp, 0);
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return result;
}
/*模块卸载函数*/
static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev); /*注销cdev*/
kfree(globalmem_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
}
MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalmem_major, int, S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);
测试驱动函数的代码
/*包含操作系统的头文件*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
/*包含C库的头文件*/
#include <stdio.h>
/*包含第三方库的头文件*/
/*包含自定义的头文件*/
int main(void)
{
int fd=-1;
char buf[20]="1234567890123456789";
char outbuf[20]="";
fd=open("/dev/globalmem",O_RDWR);
if(fd<0)
{
printf("Open failed\n");
return 1;
}
write(fd,buf,20);
lseek(fd,0,SEEK_SET);
read(fd,outbuf,20);
printf("Outbuf:%s\n",outbuf);
ioctl(fd,1,0);
write(fd,"hello",6);
lseek(fd,0,SEEK_SET);
read(fd,outbuf,6);
printf("Outbuf:%s\n",outbuf);
close(fd);
return 0;
}
- 运行结果,首先将globalmem驱动函数下载到开发板的
- 运行结果,再者将写好的测试驱动程序的代码编译,如下编译,-Wall,能很好的把一些看不到的警告显示出来
- 运行结果,再将测试代码下载到开发板里,结果出现顺序乱掉是因为驱动程序里的printk函数的优先级比printf的优先级高
1.以上的代码的问题:读和写只能非阻塞,不能阻塞(即程序不能进行睡眠)
2.如果同时打开两个设备文件的话,则会发生进程问题
升级版
1.驱动函数
/*======================================================================
A globalfifo driver as an example of char device drivers
This example is to introduce poll,blocking and non-blocking access
The initial developer of the original code is Baohua Song
<author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/slab.h>
#define GLOBALFIFO_SIZE 0x1000 /*È«ŸÖfifo×îŽó4K×ÖœÚ*/
#define FIFO_CLEAR 0x1 /*Çå0È«ŸÖÄÚŽæµÄ³€¶È*/
#define GLOBALFIFO_MAJOR 253 /*Ô€ÉèµÄglobalfifoµÄÖ÷É豞ºÅ*/
static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoÉ豞œá¹¹Ìå*/
struct globalfifo_dev
{
struct cdev cdev; /*cdevœá¹¹Ìå*/
unsigned int current_len; /*¿¿¿¿*/
unsigned char mem[GLOBALFIFO_SIZE]; /*¿¿¿¿*/
wait_queue_head_t r_wait; /*¿¿¿¿¿*/
wait_queue_head_t w_wait; /*¿¿¿¿¿*/
};
struct globalfifo_dev *globalfifo_devp; /*É豞œá¹¹ÌåÖžÕë*/
/*ÎÄŒþŽò¿ªº¯Êý*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
/*œ«É豞œá¹¹ÌåÖžÕëž³ÖµžøÎÄŒþËœÓÐÊýŸÝÖžÕë*/
filp->private_data = globalfifo_devp;
return 0;
}
/*ÎÄŒþÊͷź¯Êý*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctlÉ豞¿ØÖƺ¯Êý */
static long globalfifo_ioctl(struct file *filp, unsigned
int cmd, unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
switch (cmd)
{
case FIFO_CLEAR:
dev->current_len = 0;
memset(dev->mem,0,GLOBALFIFO_SIZE);
printk(KERN_INFO "globalfifo is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
struct globalfifo_dev *dev = filp->private_data; /*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
poll_wait(filp, &dev->r_wait, wait);
poll_wait(filp, &dev->w_wait, wait);
/*fifo·Ç¿Õ*/
if (dev->current_len != 0)//¿¿¿¿¿¿¿0
{
mask |= POLLIN | POLLRDNORM;//¿¿¿¿|¿¿
}
/*fifo·ÇÂú*/
if (dev->current_len != GLOBALFIFO_SIZE)//¿¿¿¿¿¿¿¿¿¿¿
{
mask |= POLLOUT | POLLWRNORM; /*¿¿¿¿|¿¿*/
}
return mask;
}
/*globalfifo¶Áº¯Êý*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
int ret;
struct globalfifo_dev *dev = filp->private_data; //»ñµÃÉ豞œá¹¹ÌåÖžÕë
DECLARE_WAITQUEUE(wait, current); //¿¿¿¿¿¿,¿¿¿THis¿¿,¿¿¿¿wait¿¿
add_wait_queue(&dev->r_wait, &wait); //¿¿¿¿¿¿¿¿¿¿
/**/
//printk("Driver:"KERN_INFO"Have no data in device!\n");
//¿¿¿¿¿1,¿¿¿¿¿¿,¿¿¿0,¿¿¿¿¿
//¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿0,¿¿¿¿¿¿,¿read¿¿¿¿¿¿
if((filp->f_flags & O_NONBLOCK) && dev->current_len == 0){
//printk("Driver:"KERN_INFO"device is set as NONBLOACK!\n");
ret = - EAGAIN;
//¿¿¿¿¿¿¿goto
goto out;
//¿¿¿¿¿¿¿,¿¿¿¿¿¿¿,¿¿¿¿¿
}else if(!(filp->f_flags & O_NONBLOCK)){
//printk("Driver:"KERN_INFO"device is set as BLOACK!\n");
//printk("Driver:"KERN_INFO"Before schedule function!\n");
//¿¿¿¿¿¿¿¿¿,¿¿¿¿,interruptible¿¿¿¿,¿¿dev->current_len>0¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿write¿¿,¿¿dev_current_len¿¿¿¿¿¿¿¿
wait_event_interruptible(dev->r_wait,dev->current_len > 0);
//printk("Driver:"KERN_INFO"After schedule function!\n");
if (signal_pending(current))//¿¿¿¿¿¿¿¿¿¿¿¿,¿
//Èç¹ûÊÇÒòΪÐźŻœÐÑ
{
ret = - ERESTARTSYS;
goto out;
}
}
//printk("Driver:"KERN_INFO"Start read data!\n");
/*¿¿¿¿¿¿¿¿¿¿¿*/
if (count > dev->current_len)
count = dev->current_len;//¿¿¿¿¿¿¿¿¿
if (copy_to_user(buf, dev->mem, count))
{
ret = - EFAULT;
goto out;
}
else
{
//¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿¿
memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifoÊýŸÝÇ°ÒÆ
dev->current_len -= count; //ÓÐЧÊýŸÝ³€¶ÈŒõÉÙ
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
wake_up_interruptible(&dev->w_wait); //¿¿¿¿¿¿¿,¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿
ret = count;
}
out:
remove_wait_queue(&dev->r_wait, &wait); //
set_current_state(TASK_RUNNING);//¿¿¿¿¿¿¿¿¿¿¿¿¿
return ret;
}
/*globalfifoÐŽ²Ù×÷*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct globalfifo_dev *dev = filp->private_data; //»ñµÃÉ豞œá¹¹ÌåÖžÕë
int ret;
DECLARE_WAITQUEUE(wait, current); //¶šÒåµÈŽý¶ÓÁÐ
add_wait_queue(&dev->w_wait, &wait); //œøÈëÐŽµÈŽý¶ÓÁÐÍ·
if ((filp->f_flags & O_NONBLOCK)&& (dev->current_len >= GLOBALFIFO_SIZE))
{
ret = - EAGAIN;
goto out;
}
else if(!(filp->f_flags & O_NONBLOCK))
{
//¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿,¿¿¿¿,¿¿¿¿¿¿¿¿¿
wait_event_interruptible(dev->w_wait,dev->current_len < GLOBALFIFO_SIZE);
if (signal_pending(current))//¿¿¿¿¿¿¿¿¿¿,¿¿¿¿¿
{
ret = - ERESTARTSYS;
goto out;
}
}
/**/
if (count > GLOBALFIFO_SIZE - dev->current_len)
count = GLOBALFIFO_SIZE - dev->current_len;
//¿¿¿¿¿¿¿
if (copy_from_user(dev->mem + dev->current_len, buf, count))
{
ret = - EFAULT;
goto out;
}
else
{
dev->current_len += count;
printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
->current_len);
wake_up_interruptible(&dev->r_wait); //¿¿¿¿¿¿
ret = count;//¿¿¿¿¿¿¿¿¿
}
out:
remove_wait_queue(&dev->w_wait, &wait); //
set_current_state(TASK_RUNNING);//¿¿¿¿¿¿¿¿¿¿¿¿¿
return ret;
}
/*ÎÄŒþ²Ù×÷œá¹¹Ìå*/
static const struct file_operations globalfifo_fops =
{
.owner = THIS_MODULE,
.read = globalfifo_read,
.write = globalfifo_write,
.unlocked_ioctl = globalfifo_ioctl,
.poll = globalfifo_poll,//¿¿¿¿¿¿¿¿
.open = globalfifo_open,
.release = globalfifo_release,
};
/*³õÊŒ»¯²¢×¢²ácdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
int err, devno = MKDEV(globalfifo_major, index);
cdev_init(&dev->cdev, &globalfifo_fops);
dev->cdev.owner = THIS_MODULE;
// dev->cdev.ops = &globalfifo_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
/*É豞Çý¶¯Ä£¿éŒÓÔغ¯Êý*/
static __init int globalfifo_init(void)
{
int ret = 0;
dev_t devno = MKDEV(globalfifo_major, 0);
/* ÉêÇëÉ豞ºÅ*/
if (globalfifo_major)
ret = register_chrdev_region(devno, 1, "globalfifo");
if(ret < 0) /* ¶¯Ì¬ÉêÇëÉ豞ºÅ */
{
ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
globalfifo_major = MAJOR(devno);
}
if (ret < 0)
return ret;
/* ¶¯Ì¬ÉêÇëÉ豞œá¹¹ÌåµÄÄÚŽæ*/
globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
if (!globalfifo_devp) /*ÉêÇëʧ°Ü*/
{
ret = - ENOMEM;
goto fail_malloc;
}
memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
globalfifo_setup_cdev(globalfifo_devp, 0);
init_waitqueue_head(&globalfifo_devp->r_wait); /*¿¿¿¿¿¿¿*/
init_waitqueue_head(&globalfifo_devp->w_wait); /**/
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return ret;
}
/*Ä£¿éжÔغ¯Êý*/
static __exit void globalfifo_exit(void)
{
cdev_del(&globalfifo_devp->cdev); /*×¢Ïúcdev*/
kfree(globalfifo_devp); /*ÊÍ·ÅÉ豞œá¹¹ÌåÄÚŽæ*/
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*ÊÍ·ÅÉ豞ºÅ*/
}
MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalfifo_major, int, S_IRUGO);
module_init(globalfifo_init);
module_exit(globalfifo_exit);
数据结构
- 对同类型的元素进行管理的学科
1.元素之间的逻辑关系(1)同属一个集合 (2)元素之间存在一对一的关系:线性关系,线性表(栈(stack):先进后出(在一端做插入,同一端做删除),队列(queue):先进先出(在一端做插入,在另一端做删除)) (3)一对多的关系:树形关系 (4)多对多的关系:图形关系
2.元素之间的存储方式(1)顺序存储(数组定义,动态分配)(2)链式存储(节点的空间只能动态分配)
select(多功能复用)
最终版
1.驱动函数
/*======================================================================
A globalfifo driver as an example of char device drivers
This example is to introduce poll,blocking and non-blocking access
The initial developer of the original code is Baohua Song
<author@linuxdriver.cn>. All Rights Reserved.
======================================================================*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
#include <linux/slab.h>
#define GLOBALFIFO_SIZE 0x1000 /*È«ŸÖfifo×îŽó4K×ÖœÚ*/
#define FIFO_CLEAR 0x1 /*Çå0È«ŸÖÄÚŽæµÄ³€¶È*/
#define GLOBALFIFO_MAJOR 253 /*Ô€ÉèµÄglobalfifoµÄÖ÷É豞ºÅ*/
static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoÉ豞œá¹¹Ìå*/
struct globalfifo_dev
{
struct cdev cdev; /*cdevœá¹¹Ìå*/
unsigned int current_len; /*fifoÓÐЧÊýŸÝ³€¶È*/
unsigned char mem[GLOBALFIFO_SIZE]; /*È«ŸÖÄÚŽæ*/
struct semaphore sem; /*²¢·¢¿ØÖÆÓõÄÐźÅÁ¿*/
wait_queue_head_t r_wait; /*×èÈû¶ÁÓõĵȎý¶ÓÁÐÍ·*/
wait_queue_head_t w_wait; /*×èÈûÐŽÓõĵȎý¶ÓÁÐÍ·*/
};
struct globalfifo_dev *globalfifo_devp; /*É豞œá¹¹ÌåÖžÕë*/
/*ÎÄŒþŽò¿ªº¯Êý*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
/*œ«É豞œá¹¹ÌåÖžÕëž³ÖµžøÎÄŒþËœÓÐÊýŸÝÖžÕë*/
filp->private_data = globalfifo_devp;
return 0;
}
/*ÎÄŒþÊͷź¯Êý*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctlÉ豞¿ØÖƺ¯Êý */
static long globalfifo_ioctl(struct file *filp, unsigned
int cmd, unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;/*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
switch (cmd)
{
case FIFO_CLEAR:
down(&dev->sem); //¿¿¿¿p¿¿
dev->current_len = 0;
memset(dev->mem,0,GLOBALFIFO_SIZE);//¿¿¿¿¿¿
up(&dev->sem); //¿¿¿¿v¿¿
printk(KERN_INFO "globalfifo is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
struct globalfifo_dev *dev = filp->private_data; /*»ñµÃÉ豞œá¹¹ÌåÖžÕë*/
down(&dev->sem);
poll_wait(filp, &dev->r_wait, wait);
poll_wait(filp, &dev->w_wait, wait);
/*fifo·Ç¿Õ*/
if (dev->current_len != 0)
{
mask |= POLLIN | POLLRDNORM; /*±êÊŸÊýŸÝ¿É»ñµÃ*/
}
/*fifo·ÇÂú*/
if (dev->current_len != GLOBALFIFO_SIZE)
{
mask |= POLLOUT | POLLWRNORM; /*±êÊŸÊýŸÝ¿ÉÐŽÈë*/
}
up(&dev->sem);
return mask;
}
/*globalfifo¶Áº¯Êý*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
int ret;
struct globalfifo_dev *dev = filp->private_data; //
DECLARE_WAITQUEUE(wait, current); //¶šÒåµÈŽý¶ÓÁÐ
down(&dev->sem); //p¿¿,¿¿¿¿p¿¿¿¿¿¿¿,¿¿¿¿
add_wait_queue(&dev->r_wait, &wait); //œøÈë¶ÁµÈŽý¶ÓÁÐÍ·
/* µÈŽýFIFO·Ç¿Õ */
if (dev->current_len == 0)//¿¿¿¿¿¿
{
printk("Driver:"KERN_INFO"Have no data in device!\n");
if (filp->f_flags &O_NONBLOCK)//¿¿¿¿¿¿¿¿¿¿
{
printk("Driver:"KERN_INFO"device is set as NONBLOACK!\n");
ret = - EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); //¿¿¿¿¿¿¿¿¿¿¿¿
up(&dev->sem);//¿¿¿¿¿¿¿,¿¿¿¿¿¿¿¿¿¿¿¿¿¿
printk("Driver:"KERN_INFO"device is set as BLOCK!\n");
printk("Driver:"KERN_INFO"Before schedule function!\n");
schedule(); //¿¿¿¿¿¿¿¿¿
printk("Driver:"KERN_INFO"After schedule function!\n");
if (signal_pending(current))//¿¿¿¿¿¿¿¿¿
//Èç¹ûÊÇÒòΪÐźŻœÐÑ
{
ret = - ERESTARTSYS;
goto out2;
}
down(&dev->sem);//¿¿p¿¿
}
printk("Driver:"KERN_INFO"Start read data!\n");
/* ¿œ±ŽµœÓû§¿ÕŒä */
if (count > dev->current_len)
count = dev->current_len;
if (copy_to_user(buf, dev->mem, count))
{
ret = - EFAULT;
goto out;
}
else
{
memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifoÊýŸÝÇ°ÒÆ
dev->current_len -= count; //ÓÐЧÊýŸÝ³€¶ÈŒõÉÙ
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
wake_up_interruptible(&dev->w_wait); //»œÐÑÐŽµÈŽý¶ÓÁÐ
ret = count;
}
out: up(&dev->sem); //ÊÍ·ÅÐźÅÁ¿
out2:remove_wait_queue(&dev->w_wait, &wait); //ŽÓžœÊôµÄµÈŽý¶ÓÁÐÍ·ÒƳý
set_current_state(TASK_RUNNING);
return ret;
}
/*globalfifoÐŽ²Ù×÷*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
struct globalfifo_dev *dev = filp->private_data; //»ñµÃÉ豞œá¹¹ÌåÖžÕë
int ret;
DECLARE_WAITQUEUE(wait, current); //¶šÒåµÈŽý¶ÓÁÐ
down(&dev->sem); //»ñÈ¡ÐźÅÁ¿
add_wait_queue(&dev->w_wait, &wait); //œøÈëÐŽµÈŽý¶ÓÁÐÍ·
/* µÈŽýFIFO·ÇÂú */
if (dev->current_len == GLOBALFIFO_SIZE)
{
if (filp->f_flags &O_NONBLOCK)
//Èç¹ûÊÇ·Ç×èÈû·ÃÎÊ
{
ret = - EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); //žÄ±äœø³Ì׎̬Ϊ˯Ãß
up(&dev->sem);
schedule(); //µ÷¶ÈÆäËûœø³ÌÖŽÐÐ
if (signal_pending(current))
//Èç¹ûÊÇÒòΪÐźŻœÐÑ
{
ret = - ERESTARTSYS;
goto out2;
}
down(&dev->sem); //»ñµÃÐźÅÁ¿
}
/*ŽÓÓû§¿ÕŒä¿œ±ŽµœÄں˿Ռä*/
if (count > GLOBALFIFO_SIZE - dev->current_len)
count = GLOBALFIFO_SIZE - dev->current_len;
if (copy_from_user(dev->mem + dev->current_len, buf, count))
{
ret = - EFAULT;
goto out;
}
else
{
dev->current_len += count;
printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev
->current_len);
wake_up_interruptible(&dev->r_wait); //»œÐѶÁµÈŽý¶ÓÁÐ
ret = count;
}
out: up(&dev->sem); //ÊÍ·ÅÐźÅÁ¿
out2:remove_wait_queue(&dev->w_wait, &wait); //ŽÓžœÊôµÄµÈŽý¶ÓÁÐÍ·ÒƳý
set_current_state(TASK_RUNNING);
return ret;
}
/*ÎÄŒþ²Ù×÷œá¹¹Ìå*/
static const struct file_operations globalfifo_fops =
{
.owner = THIS_MODULE,
.read = globalfifo_read,
.write = globalfifo_write,
.unlocked_ioctl = globalfifo_ioctl,
.poll = globalfifo_poll,
.open = globalfifo_open,
.release = globalfifo_release,
};
/*³õÊŒ»¯²¢×¢²ácdev*/
static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
{
int err, devno = MKDEV(globalfifo_major, index);
cdev_init(&dev->cdev, &globalfifo_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &globalfifo_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
/*É豞Çý¶¯Ä£¿éŒÓÔغ¯Êý*/
static __init int globalfifo_init(void)
{
int ret = 0;
dev_t devno = MKDEV(globalfifo_major, 0);
/* ÉêÇëÉ豞ºÅ*/
if (globalfifo_major)
ret = register_chrdev_region(devno, 1, "globalfifo");
if(ret < 0) /* ¶¯Ì¬ÉêÇëÉ豞ºÅ */
{
ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
globalfifo_major = MAJOR(devno);
}
if (ret < 0)
return ret;
/* ¶¯Ì¬ÉêÇëÉ豞œá¹¹ÌåµÄÄÚŽæ*/
globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
if (!globalfifo_devp) /*ÉêÇëʧ°Ü*/
{
ret = - ENOMEM;
goto fail_malloc;
}
memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
globalfifo_setup_cdev(globalfifo_devp, 0);
sema_init(&globalfifo_devp->sem,1); /*³õÊŒ»¯ÐźÅÁ¿*/
init_waitqueue_head(&globalfifo_devp->r_wait); /*³õÊŒ»¯¶ÁµÈŽý¶ÓÁÐÍ·*/
init_waitqueue_head(&globalfifo_devp->w_wait); /*³õÊŒ»¯ÐŽµÈŽý¶ÓÁÐÍ·*/
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return ret;
}
/*Ä£¿éжÔغ¯Êý*/
static __exit void globalfifo_exit(void)
{
cdev_del(&globalfifo_devp->cdev); /*×¢Ïúcdev*/
kfree(globalfifo_devp); /*ÊÍ·ÅÉ豞œá¹¹ÌåÄÚŽæ*/
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*ÊÍ·ÅÉ豞ºÅ*/
}
MODULE_AUTHOR("Hiro Wang");
MODULE_LICENSE("Dual BSD/GPL");
module_param(globalfifo_major, int, S_IRUGO);
module_init(globalfifo_init);
module_exit(globalfifo_exit);
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
1.int nfds的值为:最大的文件描述符的下标+1,即为有几个文件描述符
2.fd_set:是一个结构体的类型
- 简单代替读写操作的命令
(1) cat /dev/设备文件/ (读操作)
(2)sudo echo afghajhi > /dev/globalfifo (写操作)
(3)select 则要写测试代码(调用select函数)
open 设备文件
while(1)
{
FD_CLR
FD_SET
select();//返回值为设备文件有数据可读的个数
if(FD_ISSET())
{
}
if(FD_ISSET())
{
}
}
判断返回值(<0错误,>0正确,=0)
- 测试驱动程序的读和写,运行结果为如下
LED驱动函数
1.GPIO:IO口,通用输入/输出
驱动函数
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define DEVICE_NAME "leds"
static unsigned long led_table [] = {
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static unsigned int led_cfg_table [] = {
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
static int sbc2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = sbc2440_leds_ioctl,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
int i;
for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 0);
}
ret = misc_register(&misc);
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
测试代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int cmd=-1;
int ledno=-1;
int fd=-1;
if(argc<3)
{
printf("Usage is testleds 0 2\n");
return 1;
}
sscanf(argv[1],"%d",&cmd);
sscanf(argv[2],"%d",&ledno);
if(cmd<0 ||cmd>1)
{
printf("The second arguments is error\n");
return 2;
}
if(ledno<1||ledno>4)
{
printf("The third arguments is error\n");
return 3;
}
ledno--;
fd=open("/dev/leds",O_RDWR);
if(fd<0)
{
printf("Open /dev/leds failed\n");
return 4;
}
ioctl(fd,cmd,ledno);
close(fd);
return 0;
}
- 运行结果为
volatile
1.是可变的关键字
- 一个整型变量,如果没有前面的关键字,编译器可以会用寄存器去访问,提高效率,但当另一个进程要进行修改这个变量时,此时这两个得到的值就不同,所以不要编译器帮我们进行优化,此时要加volatile关键字