/****************************************************************
You Must Believe You Can Do It.
So,You Can Do It Best!
Time :2016/12/30
****************************************************************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/string.h>
#include <linux/slab.h>
//操作底层的驱动io的函数
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
MODULE_LICENSE("GPL");
/****************************************************************
这里是ioctl函数 的声明的宏
*/
#define LED_OFF _IO('Z',0)
#define LED_ON _IO('Z',1)
#define ALL_LED_OFF _IO('Z',2)
#define ALL_LED_ON _IO('Z',3)
#define THE_READ_REMAINING_BYTE _IOR('R',1,int)
#define THE_WRITE_REMAINING_BYTE _IOW('W',1,int)
/****************************************************************
这里是声明buf的大小和是否采用class和device的方式
*/
#define DEVICE_CLASS 1
#define MAX_FIFO_SIZE 4096
/****************************************************************
这里设置成LED寄存器控制的数组
*/
unsigned long led_num[4] =
{
S5PV210_GPJ2(0),
S5PV210_GPJ2(1),
S5PV210_GPJ2(2),
S5PV210_GPJ2(3),
};
/****************************************************************
定义一个FIFO的数组 满足先进先出和循环使用的buf结构体
*/
struct fifo_buf
{
unsigned int rpos; /*读位置*/
unsigned int wpos; /*写位置*/
unsigned int max_len; /*缓冲区的总长度*/
unsigned int avail_len; /*可读的长度*/
unsigned char *data; /*缓冲区指针*/
};
struct fifo_buf *pbuf = NULL;
#ifdef DEVICE_CLASS
struct class *test_class = NULL;
struct device *test_device = NULL;
#endif
struct cdev test_cdev;
uint devno_major = 0;
uint devno_minor = 0;
/****************************************************************
初始化LED寄存器的函数
*/
static void Led_init_fifo(void)
{
int i,j;
for(i=0;i<4;i++)
{
//这是注册gpio口
j = gpio_request(led_num[i],"GPH_J");
if(j)
{
printk(KERN_ERR "gpio register is error\n");
return ;
}
//设置成输出模式
gpio_direction_output(led_num[i],1);
//一开始将灯全部熄灭
__gpio_set_value(led_num[i],1);
} //end for()
}//end Led_init_fifo
/****************************************************************
这下面的函数 驱动底层调用函数
*/
int test_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "%s\n",__FUNCTION__);
return 0;
}
int test_close (struct inode *inode, struct file *file)
{
printk (KERN_INFO "%s\n",__FUNCTION__);
return 0;
}
ssize_t test_read(struct file *filp,char __user *buf, size_t count, loff_t *f_pos)
{
int want_rNum; //本次应该要读取的字节数
int cur_r = 0; //本次成功读取的字节数
int remain; //超出的部分
int r; //作为保存数据用的
struct fifo_buf *p = NULL;
printk(KERN_INFO"pipe_read\n");
p = pbuf;
if(!p)
{
return -1;
}
want_rNum = min(count,p->avail_len);
if(!want_rNum)
{
return 0;
}
//这个是表示 读的偏移位置在写的前面
if(p->rpos < p->wpos)
{
cur_r =want_rNum - copy_to_user(buf,p->data+p->rpos,want_rNum);
p->rpos += cur_r;
}
else //这个表示 读的偏移位置在写的后面或者相同 则会有另外两种方式 越界和不越界
{
//这个表示没有越界
if((p->max_len - p->rpos) > want_rNum)
{
cur_r = want_rNum - copy_to_user(buf,p->data+p->rpos,want_rNum);
p->rpos += cur_r;
}
else //存在越界的情况
{
//越界则需要回环
cur_r = want_rNum - copy_to_user(buf,p->data+p->rpos,want_rNum);
if (cur_r != p->max_len - p->rpos) //这里是表示刚刚好到达边界或者还没有到达
{
p->rpos += cur_r;
}
else
{
remain = want_rNum - (p->max_len - p->rpos);
p->rpos = 0;
if(remain)
{
r = remain - copy_to_user(buf,p->data+p->rpos,remain);
cur_r += r;
p->rpos += r;
}
}
}
}
//这里是avail_len是能读的字节数
p->avail_len -= cur_r;
return cur_r; //符合 user层的返回值
}
/****************************************************************
这个写操作和上面的读操作时异曲同工之妙
参照上面的注释即可理解
*/
ssize_t test_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
int want_wNum;
int cur_w = 0;
int w2;
int remain;
struct fifo_buf *p = pbuf;
printk(KERN_INFO" fifo_write\n");
if (!pbuf)
return -1;
/*w 本次应该要写的字节数*/
want_wNum= min(count, p->max_len - p->avail_len);
if (want_wNum == 0)
return 0;
if (p->wpos < p->rpos) {
/*want_wNum = 应该要写的字节数 - 没写进去的字节数
=> cur_w 为本次实际写成功的字节数*/
cur_w = want_wNum - copy_from_user(p->data + p->wpos, buf, want_wNum);
p->wpos += cur_w;
} else {
if (p->max_len - p->wpos > want_wNum) {
cur_w = want_wNum - copy_from_user(p->data + p->wpos,buf,want_wNum);
p->wpos += cur_w;
} else {
cur_w = (p->max_len - p->wpos) -
copy_from_user(p->data + p->wpos, buf, p->max_len - p->wpos);
if (cur_w != p->max_len - p->wpos)
p->wpos += cur_w;
else {
remain = want_wNum - (p->max_len - p->wpos);
p->wpos = 0;
if (remain) {
w2 = remain - copy_from_user(p->data, buf + want_wNum - remain, remain);
p->wpos += w2;
cur_w += w2;
}
}
}
}
/*cur_w为本次成功写入管道的字节数*/
p->avail_len += cur_w;//管道的可读字节数又多了w1
/*write函数应该返回本次成功写进去的字节数*/
return cur_w;
}
/****************************************************************
用户层可以通过这个操作来实现控制灯的操作
也就相当于一个外部接口
*/
long test_ioctl(struct file *_file,unsigned int cmd,unsigned long arg)
{
int err = 0;
int * __user argp;
int z;
int k;
argp = (int *)arg;
k = *argp;
if(_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,(void __user *)arg,_IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,(void __user *)arg,_IOC_SIZE(cmd));
if(err)
return -EFAULT;
switch(cmd)
{
case THE_READ_REMAINING_BYTE:
{
printk(KERN_INFO "WELCOME TO MY KERNER'S WORLD\n");
//返回还有多少个字节可以读
int *__user argp = (int *)arg;
int av_len = pbuf->avail_len;
err = put_user(av_len,argp);
printk(KERN_INFO "err = %d\n",err);
break;
}
case THE_WRITE_REMAINING_BYTE:
{
//返回还有多少个字节可以写;
int *__user argp = (int *)arg;
int av_len = pbuf->max_len - pbuf->avail_len;
err = put_user(av_len,argp);
printk(KERN_INFO "err = %d\n",err);
break;
}
case LED_OFF:
gpio_set_value(S5PV210_GPJ2(k),1); //熄灭某个灯
break;
case LED_ON:
gpio_set_value(S5PV210_GPJ2(k),0); //点亮某个灯
break;
case ALL_LED_OFF:
for(z = 0; z < 4; z++)
gpio_set_value(S5PV210_GPJ2(z),1); //全部熄灭
break;
case ALL_LED_ON:
for(z = 0; z < 4; z++)
gpio_set_value(S5PV210_GPJ2(z),0); //全部点亮
break;
default:
break;
}
}
static struct fifo_buf* init_fifo_buf(int size)
{
struct fifo_buf *p = NULL;
p = kmalloc(sizeof(struct fifo_buf),GFP_KERNEL);
if(IS_ERR(p))
{
printk(KERN_ERR "failed to create space\n");
return NULL;
}
p->data = kmalloc(size, GFP_KERNEL);
p->rpos = p->wpos = p->avail_len = 0;
p->max_len = size;
return p;
}
/*********************************************************************
这是底层操作的函数指针的结构体
并且进行初始化
*/
struct file_operations test_fops =
{
.owner = THIS_MODULE,
.open = test_open,
.release = test_close,
.read = test_read,
.write = test_write,
.unlocked_ioctl = test_ioctl,
};
static int __init fifo_init(void)
{
dev_t devno;
int ret;
//step 1:申请字符设备号
#ifdef MAJOR_NUMBER
devno = MKDEV(devno_major,devno_minor);
ret = register_chrdev_region(devno,1,"
评论0
最新资源