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驅動函數下載到開發板的
Paste_Image.png
- 運行結果,再者將寫好的測試驅動程序的代碼編譯,如下編譯,-Wall,能很好的把一些看不到的警告顯示出來
Paste_Image.png
- 運行結果,再將測試代碼下載到開發板里,結果出現順序亂掉是因為驅動程序里的printk函數的優先級比printf的優先級高
Paste_Image.png
Paste_Image.png
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è????ú??μ?3€?è*/
#define GLOBALFIFO_MAJOR 253 /*?€éèμ?globalfifoμ??÷éè±?o?*/
static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoéè±??á11ì?*/
struct globalfifo_dev
{
struct cdev cdev; /*cdev?á11ì?*/
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; /*éè±??á11ì?????*/
/*???t?ò?aoˉêy*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
/*??éè±??á11ì??????3?μ?????t??óDêy?Y????*/
filp->private_data = globalfifo_devp;
return 0;
}
/*???têí·?oˉêy*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctléè±?????oˉêy */
static long globalfifo_ioctl(struct file *filp, unsigned
int cmd, unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;/*??μ?éè±??á11ì?????*/
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; /*??μ?éè±??á11ì?????*/
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?áoˉêy*/
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; //??μ?éè±??á11ì?????
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))//????????????,?
//è?1?ê?òò?aD?o???D?
{
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êy?Y?°ò?
dev->current_len -= count; //óDD§êy?Y3€?è??éù
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;
}
/*globalfifoD?2ù×÷*/
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; //??μ?éè±??á11ì?????
int ret;
DECLARE_WAITQUEUE(wait, current); //??ò?μè?y?óáD
add_wait_queue(&dev->w_wait, &wait); //??è?D?μè?y?óáDí·
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;
}
/*???t2ù×÷?á11ì?*/
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,
};
/*3?ê??ˉ2¢×¢2á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);
}
/*éè±??y?ˉ?£?é?ó??oˉêy*/
static __init int globalfifo_init(void)
{
int ret = 0;
dev_t devno = MKDEV(globalfifo_major, 0);
/* éê??éè±?o?*/
if (globalfifo_major)
ret = register_chrdev_region(devno, 1, "globalfifo");
if(ret < 0) /* ?ˉì?éê??éè±?o? */
{
ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
globalfifo_major = MAJOR(devno);
}
if (ret < 0)
return ret;
/* ?ˉì?éê??éè±??á11ì?μ??ú??*/
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;
}
/*?£?éD???oˉêy*/
static __exit void globalfifo_exit(void)
{
cdev_del(&globalfifo_devp->cdev); /*×¢?úcdev*/
kfree(globalfifo_devp); /*êí·?éè±??á11ì??ú??*/
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*êí·?éè±?o?*/
}
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è????ú??μ?3€?è*/
#define GLOBALFIFO_MAJOR 253 /*?€éèμ?globalfifoμ??÷éè±?o?*/
static int globalfifo_major = GLOBALFIFO_MAJOR;
/*globalfifoéè±??á11ì?*/
struct globalfifo_dev
{
struct cdev cdev; /*cdev?á11ì?*/
unsigned int current_len; /*fifoóDD§êy?Y3€?è*/
unsigned char mem[GLOBALFIFO_SIZE]; /*è????ú??*/
struct semaphore sem; /*2¢·¢????ó?μ?D?o?á?*/
wait_queue_head_t r_wait; /*×èè??áó?μ?μè?y?óáDí·*/
wait_queue_head_t w_wait; /*×èè?D?ó?μ?μè?y?óáDí·*/
};
struct globalfifo_dev *globalfifo_devp; /*éè±??á11ì?????*/
/*???t?ò?aoˉêy*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
/*??éè±??á11ì??????3?μ?????t??óDêy?Y????*/
filp->private_data = globalfifo_devp;
return 0;
}
/*???têí·?oˉêy*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* ioctléè±?????oˉêy */
static long globalfifo_ioctl(struct file *filp, unsigned
int cmd, unsigned long arg)
{
struct globalfifo_dev *dev = filp->private_data;/*??μ?éè±??á11ì?????*/
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; /*??μ?éè±??á11ì?????*/
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; /*±êê?êy?Y?é??μ?*/
}
/*fifo·??ú*/
if (dev->current_len != GLOBALFIFO_SIZE)
{
mask |= POLLOUT | POLLWRNORM; /*±êê?êy?Y?éD?è?*/
}
up(&dev->sem);
return mask;
}
/*globalfifo?áoˉêy*/
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); //??ò?μè?y?óáD
down(&dev->sem); //p??,????p???????,????
add_wait_queue(&dev->r_wait, &wait); //??è??áμè?y?óáDí·
/* μè?yFIFO·??? */
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))//?????????
//è?1?ê?òò?aD?o???D?
{
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êy?Y?°ò?
dev->current_len -= count; //óDD§êy?Y3€?è??éù
printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);
wake_up_interruptible(&dev->w_wait); //??D?D?μè?y?óáD
ret = count;
}
out: up(&dev->sem); //êí·?D?o?á?
out2:remove_wait_queue(&dev->w_wait, &wait); //?ó??ê?μ?μè?y?óáDí·ò?3y
set_current_state(TASK_RUNNING);
return ret;
}
/*globalfifoD?2ù×÷*/
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; //??μ?éè±??á11ì?????
int ret;
DECLARE_WAITQUEUE(wait, current); //??ò?μè?y?óáD
down(&dev->sem); //??è?D?o?á?
add_wait_queue(&dev->w_wait, &wait); //??è?D?μè?y?óáDí·
/* μè?yFIFO·??ú */
if (dev->current_len == GLOBALFIFO_SIZE)
{
if (filp->f_flags &O_NONBLOCK)
//è?1?ê?·?×èè?·??ê
{
ret = - EAGAIN;
goto out;
}
__set_current_state(TASK_INTERRUPTIBLE); //??±???3ì×?ì??a?ˉ??
up(&dev->sem);
schedule(); //μ÷?è??????3ì??DD
if (signal_pending(current))
//è?1?ê?òò?aD?o???D?
{
ret = - ERESTARTSYS;
goto out2;
}
down(&dev->sem); //??μ?D?o?á?
}
/*?óó??§??????±?μ??úo?????*/
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); //??D??áμè?y?óáD
ret = count;
}
out: up(&dev->sem); //êí·?D?o?á?
out2:remove_wait_queue(&dev->w_wait, &wait); //?ó??ê?μ?μè?y?óáDí·ò?3y
set_current_state(TASK_RUNNING);
return ret;
}
/*???t2ù×÷?á11ì?*/
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,
};
/*3?ê??ˉ2¢×¢2á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);
}
/*éè±??y?ˉ?£?é?ó??oˉêy*/
static __init int globalfifo_init(void)
{
int ret = 0;
dev_t devno = MKDEV(globalfifo_major, 0);
/* éê??éè±?o?*/
if (globalfifo_major)
ret = register_chrdev_region(devno, 1, "globalfifo");
if(ret < 0) /* ?ˉì?éê??éè±?o? */
{
ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
globalfifo_major = MAJOR(devno);
}
if (ret < 0)
return ret;
/* ?ˉì?éê??éè±??á11ì?μ??ú??*/
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); /*3?ê??ˉD?o?á?*/
init_waitqueue_head(&globalfifo_devp->r_wait); /*3?ê??ˉ?áμè?y?óáDí·*/
init_waitqueue_head(&globalfifo_devp->w_wait); /*3?ê??ˉD?μè?y?óáDí·*/
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return ret;
}
/*?£?éD???oˉêy*/
static __exit void globalfifo_exit(void)
{
cdev_del(&globalfifo_devp->cdev); /*×¢?úcdev*/
kfree(globalfifo_devp); /*êí·?éè±??á11ì??ú??*/
unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*êí·?éè±?o?*/
}
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)
- 測試驅動程序的讀和寫,運行結果為如下
Paste_Image.png
Paste_Image.png
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;
}
- 運行結果為
Paste_Image.png
volatile
1.是可變的關鍵字
- 一個整型變量,如果沒有前面的關鍵字,編譯器可以會用寄存器去訪問,提高效率,但當另一個進程要進行修改這個變量時,此時這兩個得到的值就不同,所以不要編譯器幫我們進行優化,此時要加volatile關鍵字