Linux 字符设备驱动 LED

来源:互联网 发布:陕西广电网络官网 编辑:程序博客网 时间:2024/05/14 13:02

驱动:不多说什么,一切尽在代码中!!!!!

/****************************************************************

You Must Believe You Can Do It.
So,You Can Do It Best!

Time :2016/12/30
****************************************************************/
/**include<>**/

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,"fifo_device");
#else
ret = alloc_chrdev_region(&devno,devno_minor,1,"fifo_device");
#endif

if(ret <0)
{
printk(KERN_ERR "register is error!\n");


return ret;
}

devno_major = MAJOR(devno);
devno_minor = MINOR(devno);
printk(KERN_INFO "major : %d  minor : %d\n",devno_major,devno_minor);

//step 2:注册字符设备
//(1)先初始化
cdev_init(&test_cdev,&test_fops);
//(2)设置成本模块的
test_cdev.owner = THIS_MODULE;
//(3)加入模块到系统里面去  也就是加入到链表中去
ret = cdev_add(&test_cdev,devno,1);
if(ret <0)
{
printk(KERN_ERR "cdev register is error!\n");


return ret;
}

//step 3:设置设备结点  也就是设置iNode
#ifdef DEVICE_CLASS
test_class = class_create(THIS_MODULE, "fifo_class");
if(IS_ERR(test_class))
{
printk(KERN_ERR "failed to create device class \n");
cdev_del(&test_cdev);
unregister_chrdev_region(devno , 1);
return -1;
}

test_device = device_create(test_class, NULL,devno,NULL,"fifo_device");
#endif

//初始化  fifo数组
pbuf = init_fifo_buf(MAX_FIFO_SIZE);

//初始化 LED的控制io口和数据io口
Led_init_fifo();

return 0;
}


static void __exit fifo_exit(void)
{
int i;
dev_t devno;
devno = MKDEV(devno_major,devno_minor);


//先取消io口
for(i=0;i<4;i++)
{
gpio_free(led_num[i]);
}


cdev_del(&test_cdev);
unregister_chrdev_region(devno , 1);


#ifdef DEVICE_CLASS
device_destroy(test_device, devno);
class_destroy(test_class);
#endif
//将数组释放
if (pbuf) 
{
if (pbuf->data)
{
kfree(pbuf->data);
}

kfree(pbuf);
}
}


/****************************************************************
这里是模块的初始化和声明作者等等。。。
*/
module_init(fifo_init);
module_exit(fifo_exit);
MODULE_AUTHOR("TangSis南");
MODULE_DESCRIPTION("A simple FIFO buf");
MODULE_SUPPORTED_DEVICE("fifo_device");


至此字符设备的驱动就写完了。。。。。








0 0
原创粉丝点击