Linux字符设备驱动——杂项设备驱动模型
文章目录
- 杂项设备驱动模型
- 杂项设备驱动的特征
- 头文件
- 核心结构
- 杂项设备注册函数
- 杂项设备注销函数
- 总结:编写杂项设备驱动的基本步骤:
- 杂项设备驱动示例代码
- 杂项设备的驱动编写方法
- 杂项设备驱动模型示例
- 驱动程序zx_misc_led.c源代码
- 应用程序app.c源代码
- 实例代码测试
杂项设备驱动模型
杂项设备驱动的特征
1、 主设备号固定不变,为10
2、 注册后会自动在/dev目录下生成设备文件
3、 使用一个核心结构struct miscdevive封装起来
杂项设备的设备号
主设备号:固定为10
次设备号:0~255
头文件
include
核心结构
需要关注的几项
struct miscdevive
{
int minor; //次设备号
coust char *name; //设备名,在/dev下的设备节点
const struct file_operations *fops; //设备文件操作方法指针(文件操作集合)
}
当次设备号为255时,会自动由内核分配一个可用的次设备号
特点:安装后,会自动在dev/目录下创建设备节点。
杂项设备注册函数
int misc_register(struct miscdevice * misc)
例:misc_register(&misc_dev);
杂项设备注销函数
int misc_deregister(struct miscdevice *misc)
例:misc_deregister (&misc_dev);
总结:编写杂项设备驱动的基本步骤:
1、 先写一个模块基本代码;
2、 增加设备模型所需要的头文件;
3、 在模块的初始化函数注册设备对应的结构体;
4、 在模块的出口注销设备对应的结构;
5、 按照对应设备模型:注册函数需要的参数,反向退出应该的结构体成员。
杂项设备驱动示例代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
static int misc_open(struct inode *pinode, struct file *pfile)
{
printk(KERN_EMERG "line:%d,%s is called\n",__LINE__,__FUNCTION__);
return 0;
}
static ssize_t misc_read (struct file *pfile, char __user *buff, size_t size, loff_t *off)
{
printk(KERN_EMERG "line:%d,%s is called\n",__LINE__,__FUNCTION__);
return 0;
}
static ssize_t misc_write(struct file *pfile, const char __user *buff, size_t size, loff_t *off)
{
printk(KERN_EMERG "line:%d,%s is called\n",__LINE__,__FUNCTION__);
return 0;
}
static int misc_release(struct inode *pinode, struct file *pfile)
{
printk(KERN_EMERG "line:%d,%s is called\n",__LINE__,__FUNCTION__);
return 0;
}
/*文件操作方法集合,名为:misc_fops*/
static const struct file_operations misc_fops = {
.read = misc_read,
.write = misc_write,
.release = misc_release,
.open = misc_open,
};
/*杂项设备核心结构,名为:misc_dev*/
static struct miscdevice misc_dev = {
.fops = &misc_fops, /* 设备文件操作方法 */
.minor = 255, /* 次设备号 */
.name = “zxlinux”, /* 设备名/dev/下的设备节点名*/
};
static int __init misc_init(void)
{
misc_register(&misc_dev); //杂项设备注册函数
printk(KERN_EMERG "misc init = 0\n");
return 0;
}
static void __exit misc_exit(void)
{
misc_deregister(&misc_dev); //杂项设备注销函数
printk(KERN_EMERG "Goodbye,misc\n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
开发板上运行结果
[root@ZX20150811 /home]# ls
misc.ko
[root@ZX20150811 /home]# insmod misc.ko
[ 5145.395000] misc init = 0
[root@ZX20150811 /home]# lsmod
Tainted: P
misc 1701 0 - Live 0xbf024000 (O)
[root@ZX20150811 /home]# ls /dev/ | grep zxlinux
zxlinux
杂项设备的驱动编写方法
针对驱动编写
1、 定义一个file_operations
2、 定义一个miscdevice
3、 在入口函数中调用 misc_register
4、 在出口函数中调用misc_deregister
5、 写一个应用程序,测试这个驱动是否按照计划运行,对比结果
核心工作是实现:file_operation
file_operations中的接口函数
杂项设备驱动模型示例
驱动程序zx_misc_led.c源代码
#include <linux/module.h> /* 驱动模块头文件 */
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/io.h> /*开发板引脚支持头文件*/
#include <asm/uaccess.h> /*copy_from_user ,copy_to_user*/
#include<linux/gpio.h> // GPIO头文件
#define DEVICE_NAME "zx_misc_led"
#define LED_NUM (4)
static unsigned int LED_pin[4] = {
EXYNOS4X12_GPM4(0),
EXYNOS4X12_GPM4(1),
EXYNOS4X12_GPM4(2),
EXYNOS4X12_GPM4(3),
};
static ssize_t misc_read (struct file *pfile, char __user *buff, size_t size, loff_t *off)
{
printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
return 0;
}
static ssize_t misc_write(struct file *filp,
const char __user *user_buf,
size_t count,
loff_t *off)
{
int ret = 0;
char buf[LED_NUM] = {0};
int i = 0;
printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
if(count == 0) //应用程序传递count=0下来,并不是错误,应该返回0
{
return 0;
}
if(count > LED_NUM) //因为板子只有4个灯,所以防止用户程序恶意破坏系统。
{
count = LED_NUM;
}
ret = copy_from_user(buf, user_buf, count); //用户空间传数据到内核空间
if(ret) //如果复制失败返回-1;
{
return -1;
}
for(i = 0; i < count; i++)
{
if(buf[i] == 1)
{
gpio_set_value(LED_pin[i],0);/* 亮 */
}
else if(buf[i] == 0)
{
gpio_set_value(LED_pin[i],1);/* 灭 */
}
}
return count;
}
static int misc_open(struct inode *pinode, struct file *pfile)
{
printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
return 0;
}
static int misc_release(struct inode *pinode, struct file *pfile)
{
printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
return 0;
}
static const struct file_operations misc_led_fops =
{
.read = misc_read,
.write = misc_write,
.release = misc_release,
.open = misc_open,
};
static struct miscdevice misc_led_dev =
{
.fops = &misc_led_fops,
.minor = 255,
.name = DEVICE_NAME,
};
static int __init misc_led__init(void)
{
int ret;
int i;
for(i = 0;i < 4; i++)
{
ret = gpio_request(LED_pin[i],"leds");
if(ret < 0)
{
printk(KERN_EMERG "gpio_request is error\n");
goto gpio_request_err;
}
gpio_direction_output(LED_pin[i],1); //设置为输出功能,并且输出高电平
}
ret = misc_register(&misc_led_dev);
if(ret)
{
goto gpio_request_err;
}
printk(KERN_EMERG "%s is OK!!!\n",__FUNCTION__);
return 0;
gpio_request_err:
for(--i;i>=0;i--)
{
gpio_free(LED_pin[i]);
}
return ret ;
}
static void __exit zx_misc_led_exit(void)
{
int i = 0;
for(i=0;i<4;i++)
{
gpio_set_value(LED_pin[i],1); //灭灯
gpio_free(LED_pin[i]); //释放GPIO
}
misc_deregister(&misc_led_dev);
printk(KERN_EMERG "Goodbye,zx_misc_led\n");
}
module_init(zx_misc_led_init);
module_exit(zx_misc_led_exit);
MODULE_LICENSE("GPL");
应用程序app.c源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define DEV_NAME "/dev/zx_misc_led"
int main(void)
{
char buf[] = {1, 0, 0, 0}; //1,亮,0表示灭,
int i = 0;
int fd;
//打开设备文件 O_RDWR, O_RDONLY, O_WRONLY,
fd = open(DEV_NAME, O_RDWR);
if(fd < 0)
{
printf("open :%s failt!\r\n", DEV_NAME);
}
while(1)
{
write(fd, buf, 4);
memset(buf, 0, 4);
buf[i++ % 4] = 1;
sleep(1);
}
return 0;
}
实例代码测试
[root@ZX20150811 /home]# ls
[root@ZX20150811 /home]# insmod zx_misc_led.ko
[ 2247.770000] zx_misc_led_init is OK!!!
[root@ZX20150811 /home]# ./app
[ 2252.910000] line:59,misc_open is call
[ 2252.910000] line:34,misc_write is call
[ 2253.910000] line:34,misc_write is call
[ 2254.910000] line:34,misc_write is call
[ 2255.915000] line:34,misc_write is call
^C[ 2256.190000] line:66,misc_release is call
[root@ZX20150811 /home]#
还没有评论,来说两句吧...