udev交叉编译
❶ 如何通过设备节点查看i2c设备
linux下生成驱动设备节点文件的方法有3个:1、手动mknod;2、利用devfs;3、利用udev
在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点。
在2.6.17以前,在/dev目录下生成设备文件很容易,
devfs_mk_bdev
devfs_mk_cdev
devfs_mk_symlink
devfs_mk_dir
devfs_remove
这几个是纯devfs的api,2.6.17以前可用,但后来devfs被sysfs+udev的形式取代,同时期sysfs文件系统可以用的api:
class_device_create_file,在2.6.26以后也不行了,现在,使用的是device_create ,从2.6.18开始可用
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, const char *fmt, …)
从2.6.26起又多了一个参数drvdata: the data to be added to the device for callbacks
不会用可以给此参数赋NULL
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, …)
下面着重讲解第三种方法udev
在驱动用加入对udev的支持主要做的就是:在驱动初始化的代码里调用class_create(…)为该设备创建一个class,再为每个设备调用device_create(…)( 在2.6较早的内核中用class_device_create)创建对应的设备。
内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
struct class和class_create(…) 以及device_create(…)都包含在在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。
struct class定义在头文件include/linux/device.h中
class_create(…)在/drivers/base/class.c中实现
device_create(…)函数在/drivers/base/core.c中实现
class_destroy(…),device_destroy(…)也在/drivers/base/core.c中实现调用过程类似如下:
static struct class *spidev_class;
/*-------------------------------------------------------------------------*/
static int __devinit spidev_probe(struct spi_device *spi)
{
…
dev =device_create(spidev_class, &spi->dev, spidev->devt,
spidev, “spidev%d.%d”,
spi->master->bus_num, spi->chip_select);
…
}
static int __devexit spidev_remove(struct spi_device *spi)
{
……
device_destroy(spidev_class, spidev->devt);
……
return 0;
}
static struct spi_driver spidev_spi = {
.driver = {
.name = “spidev”,
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
};
/*-------------------------------------------------------------------------*/
static int __init spidev_init(void)
{
…
spidev_class =class_create(THIS_MODULE, “spidev”);
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
return PTR_ERR(spidev_class);
}
…
}
mole_init(spidev_init);
static void __exit spidev_exit(void)
{
……
class_destroy(spidev_class);
……
}
mole_exit(spidev_exit);
MODULE_DESCRIPTION(“User mode SPI device interface”);
MODULE_LICENSE(“GPL”);
下面以一个简单字符设备驱动来展示如何使用这几个函数
#include <linux/mole.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
int HELLO_MAJOR = 0;
int HELLO_MINOR = 0;
int NUMBER_OF_DEVICES = 2;
struct class *my_class;
//struct cdev cdev;
//dev_t devno;
struct hello_dev {
struct device *dev;
dev_t chrdev;
struct cdev cdev;
};
static struct hello_dev *my_hello_dev = NULL;
struct file_operations hello_fops = {
.owner = THIS_MODULE
};
static int __init hello_init (void)
{
int err = 0;
struct device *dev;
my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);
if (NULL == my_hello_dev) {
printk(“%s kzalloc failed!\n”,__func__);
return -ENOMEM;
}
devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);
if (HELLO_MAJOR)
err= register_chrdev_region(my_hello_dev->chrdev, 2, “memdev”);
else
{
err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, “memdev”);
HELLO_MAJOR = MAJOR(devno);
}
if (err) {
printk(“%s alloc_chrdev_region failed!\n”,__func__);
goto alloc_chrdev_err;
}
printk(“MAJOR IS %d\n”,HELLO_MAJOR);
cdev_init(&(my_hello_dev->cdev), &hello_fops);
my_hello_dev->cdev.owner = THIS_MODULE;
err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);
if (err) {
printk(“%s cdev_add failed!\n”,__func__);
goto cdev_add_err;
}
printk (KERN_INFO “Character driver Registered\n”);
my_class =class_create(THIS_MODULE,“hello_char_class”); //类名为hello_char_class
if(IS_ERR(my_class))
{
err = PTR_ERR(my_class);
printk(“%s class_create failed!\n”,__func__);
goto class_err;
}
dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,“memdev%d”,0); //设备名为memdev
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
gyro_err(“%s device_create failed!\n”,__func__);
goto device_err;
}
printk(“hello mole initialization\n”);
return 0;
device_err:
device_destroy(my_class, my_hello_dev->chrdev);
class_err:
cdev_del(my_hello_dev->chrdev);
cdev_add_err:
unregister_chrdev_region(my_hello_dev->chrdev, 1);
alloc_chrdev_err:
kfree(my_hello_dev);
return err;
}
static void __exit hello_exit (void)
{
cdev_del (&(my_hello_dev->cdev));
unregister_chrdev_region (my_hello_dev->chrdev,1);
device_destroy(my_class, devno); //delete device node under /dev//必须先删除设备,再删除class类
class_destroy(my_class); //delete class created by us
printk (KERN_INFO “char driver cleaned up\n”);
}
mole_init (hello_init);
mole_exit (hello_exit);
MODULE_LICENSE (“GPL”);
这样,模块加载后,就能在/dev目录下找到memdev这个设备节点了。
例2:内核中的drivers/i2c/i2c-dev.c
在i2cdev_attach_adapter中调用device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
“i2c-%d”, adap->nr);
这样在dev目录就产生i2c-0 或i2c-1节点
接下来就是udev应用,udev是应用层的东西,udev需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间
udev的源码可以在去相关网站下载,然后就是对其在运行环境下的移植,指定交叉编译环境,修改Makefile下的CROSS_COMPILE,如为mipsel-linux-,DESTDIR=xxx,或直接make CROSS_COMPILE=mipsel-linux-,DESTDIR=xxx 并install
把主要生成的udevd、udevstart拷贝rootfs下的/sbin/目录内,udev的配置文件udev.conf和rules.d下的rules文件拷贝到rootfs下的/etc/目录内
并在rootfs/etc/init.d/rcS中添加以下几行:
echo “Starting udevd…”
/sbin/udevd --daemon
/sbin/udevstart
(原rcS内容如下:
# mount filesystems
/bin/mount -t proc /proc /proc
/bin/mount -t sysfs sysfs /sys
/bin/mount -t tmpfs tmpfs /dev
# create necessary devices
/bin/mknod /dev/null c 1 3
/bin/mkdir /dev/pts
/bin/mount -t devpts devpts /dev/pts
/bin/mknod /dev/audio c 14 4
/bin/mknod /dev/ts c 10 16
)
这样当系统启动后,udevd和udevstart就会解析配置文件,并自动在/dev下创建设备节点文件
❷ 有没有嵌入式开发的学习路线,越详细越好
一、学习路径
万丈高楼平地起,不管多优秀的工程师都是从小白开始的。一条清晰合理的学习路线能帮助小白们高效率的完成基础知识的储备工作,注意这里是知识的储备过程,而经验是从实践中得到的。学习路径是多种多样的,不同能力和不同基础的人有不太相同的路径,这里分享我自己的学习路径,供大家参考。
1.了解计算机原理,操作系统基础知识。了解硬盘,内存和CPU的关系,程序是如何加载到内存运行的,了解操作系统进程切换和时间片的概念。
2.学习C语言,掌握编译器基本知识,能编写简单的程序。学习硬件相关知识。
3.购买洞洞板或者面包板,配合stm32等单片机核心板及相应教材,实践IO操作,中断,定时器,ADC,UART通信,IIC通信,SPI通信,CAN通信等基本功能。在此过程中不断巩固提升C语言编程水平。
4.掌握了某一种单片机的基本编程和控制后,可以进军嵌入式操作系统的学习。在此期间可以继续使用STM32核心板,加购LCD串口液晶显示屏,不需要买带字库的显示屏,简单实用的串口显示屏就可以。然后可以从Free RTOS开始学习,这个操作系统代码少,概念清晰,易于学习操作系统的原理,也易于移植,基本上可以参考官网以及网络上的资料顺利的将操作系统移植到STM32核心板。通过FreeRTOS,可以学习嵌入式操作系统的基本原理,并可以编写LCD驱动程序来感受硬件驱动程序的概念。
5.学习嵌入式Linux操作系统,购买ARM9或以上版本的主控的开发板,要求开发板上至少有串口和网口。学习板级支持包的开发,交叉编译,GDB调试,UBOOT移植,内核移植,根文件系统制作,设备树,驱动程序编写,网络编程相关知识。
6.学习物联网相关模块的使用,可以购买ESP32核心板进行wifi,蓝牙的模块控制学习,购买其他模块实现其他小项目的练习。
通过上面的一番闭关修炼,你已经学习了嵌入式开发的主要知识架构,接下来就要多做小项目,多练习排错,才能不断积累经验。
二、学习方法
1.先整再零:
对于一个实例项目,先从整体出发,保证调通,能正常运行,出现预期结果。遇到模糊的问题先跳过,整体有了一定认识后再对个别细节进行深入了解,但不能跳过深入了解细节的环节。
2.边学边练:
开发是一类实践性很强的技能,嵌入式开发要与硬件打交道,就需要更多的动手操作和观察。
学习某一方向的嵌入式开发知识时,需要给自己的学习进行必要的“投资”,购买面包板、洞洞板、万用表、调温烙铁套装、各种器件、芯片,以至开发板。以上材料不需要一次性都买齐,可以按照当前学习的内容分阶段购买,经济条件有限的同学也不用担心,以上材料的开销除开发板之外都不贵,可网络购买。对于开发板,可以买学长学姐的二手板卡,能过测试就证明板卡是OK的。
有了学习材料,就要学以致用,例如今天学习了三极管做开关,就可以自己动手画画电路图,然后在洞洞板上实践一下,通过实际操作,加深印象的同时,也能验证自己的设计方案。
3.勤于思考和提问,网络如此发达,提问的能力和技巧我就不再赘述了。
三、技能提升建议
如果你进入的是一家规模较小的公司,那么你可能有机会接触各类技术。这是绝佳的锻炼机会,要注意不要特别深入某一方向而不关注其他技术,要知道大牛需要的是多方位的技能。
大公司的话,往往分工比较细致而明确,那就需要在完成自己工作的同时多关注项目组中其他同事遇到的问题,能协助解决最好,不能解决的要关注解决的情况和方法,多蹭经验。帮助别人的同时就是在帮助自己提高,多花时间处理实际问题是难得的经验。
不管在哪种场合工作,一定注意经验的积累,好记不如带墨,要用文字将经验记录下来,将遇到的问题详细描述清楚,没事的时候翻看一下,工作时间长了,你会发现这是一笔难得的财富。
限于篇幅,这里就不再多讲技术的细节了,希望各位读者在技术成长的过程当中都能有自己清晰的学习路径,安排好自己的学习计划,稳扎稳打!
❸ 嵌入式Linux程序设计案例与实验教程的目录
前言
教学建议
第1章LiFlUX开发基础
1.1Linux系统概述
1.1.1Linux简介
1.1.2Linux系统的特点
1.1.3Linux系统的组成
1.2Linux系统的使用
实验1.1熟悉Linux基本命令与文件目录系统
1.3全屏幕编辑器与vi
1.3.1vi简介
1.3.2基本命令
1.3.3常用操作
实验1.2全屏幕编辑器vi的使用
1.4LinuxS11ell编程
1.4.1Shell程序的编写和执行
1.4.2Shell的变量
1.4.3Shell的测试命令
1.4.4条件语句
1.4.5循环语句
1.4.6函数
实验1.3Shell脚本编程实验
第2章嵌入式Linux系统基础
2.1构建嵌入式Linux系统环境
2.1.1交叉编译
2.1.2交叉编译器
2.1.3NFS
实验2.1嵌入式Linux开发环境的建立
2.2Linuxc程序设计
2.2.1C程序设计概述
2.2.2Makefile介绍
2.2.3Makefile中的变量
2.2.4Makefile隐含规则
实验2.2Makefite与helloworld
2.3Linux多线程库编程
2.3.1多线程
2.3.2Linux下的多线程
2.3.3生产者-消费者模型简述
2.3.4缓冲区操作概述
2.3.5几个线程API
实验2.3Linux多线程使用实例生产者-消费者协议
2.4进程创建以及进程间通信
2.4.1进程概述
2.4.2进程的相关函数
2.4.3信号概述
2.4.4信号的相关函数
2.4.5管道概述
2.4.6管道的相关函数
实验2.4进程相关的应用程序设计
综合实验一嵌入式平台的进程管理模拟实验
第3章嵌入式Linux内核、引导系统和文件系统
3.1Linux内核定制、裁剪和添加
3.1.1概述
3.1.2内核目录简介
3.1.3配置文件和配置工具
3.1.4内核的编译命令
实验3.1Linux内核裁剪与编译
3.2嵌入式引导系统技术
3.2.1概述
3.2.2Linux的引导系统vivi与ubOOt
3.3文件系统的构建
3.3.1概述
3.3.2BusyBox
综合实验二软盘Linux操作系统的实现
第4章嵌入式Liflux接口设计与驱动程序
4.1驱动程序设计基础
4.1.1Linux驱动程序简介
4.1.2开发驱动程序的方法
4.1.3设备驱动程序的分类
4.1.4主设备号和次设备号
4.1.5设备文件系统(devfs)与Udevfs
实验4.1虚拟驱动模块实验
4.2AD接口驱动程序
4.2.1AD转换器
4.2.2AD转换有关参数
4.2.3ARM自带的AD转换装置
实验4.2AD接口驱动程序
4.3直流电机驱动
4.3.1直流电机介绍
4.3.2直流电机的PWM原理
4.3.3PWMTIMER结构
4.3.4基于ARM的PWM相关寄存器
4.3.5关于程序实现
实验4.3直流电机PWM驱动实验
4.4触摸屏接口设计与驱动
4.4.1触摸屏的工作原理
4.4.2触摸屏驱动芯片ADS7843
4.4.3S3C2410芯片的触摸屏相关配置寄存器
实验4.4tslib移植和使用
4.5显示接口与LinuxFrameBuffer
4.5.1FrameBuffer机制介绍
4.5.2LCD简介
实验4.5FrameBuffer实验
4.6V4L程序设计
4.6.1V4L概述
4.6.2V4L设备的体系结构
实验4.6Linux视频V4L驱动实验
4.7OSS程序设计
4.7.1oSS概述
4.7.2OSS设备的体系结构
4.7.30SS驱动分析
4.7.4OSS用户空间编程
实验4.7Linux音频OSS驱动实验
综合实验三五子棋游戏的实现
第5章嵌入式Linux开源软件移植与应用
5.1嵌入式WebServerGoAhead的移植与应用
5.1.1嵌入式web服务器
5.1.2GoAhead介绍
5.1.3GoAhead在ARM平台上的移植
5.1.4页面操作
实验5.1嵌入式WebServerGoAhead实验
5.2嵌入式WebServicegSOAP的移植与应用
5.2.1gSOAP介绍
5.2.2gSOAP裁剪
5.2.3gSOAP应用
实验5.2WebServicegSOAP实验
5.3嵌入式数据库SQLite的移植与使用
5.3.1嵌入式数据库
5.3.2SQLite介绍
5.3.3SQLite在ARM平台上的移植
5.3.4SQLite的使用
实验5.3SQLite移植实验
5.4播放器Mplayer的移植
5.4.1Mplayer介绍
5.4.2Mplayer在ARM平台上的移植
实验5.4Mplayer到ARM平台上的移植
5.5ffmpeg应用:
5.5.1ffmpeg简介
5.5.2ffmpeg在ARM上的移植
5.5.3ffmpeg命令应用实例
5.5.4ffmpeg中几个重要的数据结构
5.5.5ffmpeg应用开发
实验5.5ffmpeg移植与应用
5.6开源软件移植的一般过程
5.6.1软件移植的概念
5.6.2软件移植过程
5.7JIME—phoneME移植
5.7.1phoneME简介
5.7.2软件移植过程与效果
5.8嵌入式浏览器konqueror移植
5.8.1konqueror简介
5.8.2软件移植过程与效果
综合实验四基于WebServiee的嵌入式计算器
第6章嵌入式Linux图形用户界面
6.1嵌入式GUI简介
6.1.1嵌入式GUI的特点
6.1.2嵌入式GUI的种类
6.2嵌入式GUI—Qt
6.2.1Qt与Qt/Embedded简介
6.2.2Qt的特点
6.2.3Qt的执行过程
6.2.4Qt的插槽机制
6.2.5一个完整的Qt程序
6.2.6QtDesigner介绍
实验6.1Qt图形界面相关实验
6.3基于Qt技术的Qtopia
6.3.1Qtopia简介
6.3.2Qtopia的功能
6.3.3Qtopia编程
实验6.2Qtopia的移植以及编程
综合实验五电子点菜系统
第7章嵌入式Unux下的通信应用
7.1嵌入式Linux下的串口通信
7.1.1串口简介
7.1.2串口编程
7.1.3串口编程应用实例
实验7.1串口通信实验
7.2嵌入式Linux网络编程
7.2.1网络通信
7.2.2Socket简介
7.2.3网络编程
实验7.2Socket相关程序设计
7.3嵌入式蓝牙技术
7.3.1蓝牙技术
7.3.2蓝牙体系结构
7.3.3蓝牙通信网络
7.3.4LinuxBluetooth软件层
7.3.5USB适配器
实验7.3蓝牙相关实验
7.4CAN总线
7.4.1CAN总线简介
7.4.2CAN总线硬件特征
7.4.3CAN控制器驱动
实验7.4CAN总线实验
第8章嵌入式系统硬件设计基础与标准
8.1嵌入式系统的硬件组成
8.1.1嵌入式微处理器
8.1.2存储器
8.1.3输入/输出设备
8.1.4通信与扩展接口
8.2硬件设计基础知识
8.2.1计算机体系结构
8.2.2电子技术
8.2.3抗干扰技术
8.2.4印制电路板
8.3硬件设计中应注意的一些问题
8.3.1IC元件的选择
8.3.2元件封装设计
8.3.3PCB设计精度
8.3.4分离元件的正确使用
8.3.5高速PCB设计方法
8.3.6PCB设计的一般原则
实验8.1常用模拟电路和数字电路原理
实验8.22410—S电路原理图阅读
实验8.32410—S所用芯片数据手册阅读
实验8.4OMAP5910核心板电路原理
综合实验六基于OMAP的加密终端硬件设计
第9章OMAP5910与LinuxGateway
9.1OMAP5910体系结构
9.1.1MPU子系统
9.1.2DSP子系统
9.2LinuxDSPGateway
9.2.1DSPGateway的由来
9.2.2DSPGateway的Mailbox机制
9.2.3通信缓冲
9.2.4Mailbox命令协议
9.2.5DSPGateway的设备接口
实验9.1OMAP910双核间基本通信
9.3OMAP5910图像处理
9.3.1图片格式
9.3.2数字图像算法
实验9.2OMAP图像处理实验
综合实验七基于OMAP的加密终端的实现(软件部分)
第10章嵌入式Linux综合项目实例
10.1基于嵌入式平台的电梯监控系统
10.1.1系统概述
10.1.2系统设计
10.1.3系统实现
10.1.4项目小结
10.2基于蓝牙技术的嵌入式点菜系统
10.2.1系统概述
10.2.2系统设计
10.2.3系统实现
10.2.4项目小结
10.3基于WebSenrice的数字油田监控系统
10.3.1系统概述
10.3.2系统设计
10.3.3系统实现
10.3.4项目小结
10.4基于嵌入式与WebService的智能家居系统
10.4.1系统概述
10.4.2系统设计
10.4.3系统实现
10.4.4项目小结
10.5基于OMAP的音频与视频处理
10.5.1概述
10.5.2MPEG压缩
10.5.3音视频数据在双处理器间的传输模块设计
10.5.4音频处理方案设计
10.5.5视频处理方案设计
10.5.6项目小结
参考文献
……
❹ 交叉编译时,如何链接指定路径下的库
在整个工程的configure.in文件中加入如下代码:
#configure.in
if test x$CC = xgcc; then
#AC_PATH_PROG(BLKID, blkid, [], [$PATH:/sbin])
#AC_PATH_PROG(VOLID, vol_id, [], [$PATH:/lib/udev])
AC_MSG_WARN($BLKID --------------------------------------) #code only for test
AM_CONDITIONAL(MY_CROSS_COMPILE,false)
else
#AC_PATH_PROG(BLKID, blkid, [], [/home/user-name/ltib/rootfs/sbin])
#AC_PATH_PROG(VOLID, vol_id, [], [/home/user-name/ltib/rootfs/lib])
AC_MSG_WARN($BLKID ++++++++++++++++++++++++++++++++++++++) #code only for test
AM_CONDITIONAL(MY_CROSS_COMPILE,true)
fi
在需要blkid库的Makefile.am文件中
if MY_CROSS_COMPILE
AM_CPPFLAGS = -include $(top_builddir)/config.h -I ../include \
-DLOCALEDIR=\"$(localedir)\" -I /home/user-name/ltib/rootfs/usr/include
AM_CFLAGS = -fsigned-char -I /home/user-name/ltib/rootfs/usr/include
else
AM_CPPFLAGS = -include $(top_builddir)/config.h -I ../include \
-DLOCALEDIR=\"$(localedir)\"
AM_CFLAGS = -fsigned-char
endif
lib_mount_la_LIBADD = $(LDADD_common)
LDADD_common =
if MY_CROSS_COMPILE
LDADD_common += -L$(LTIB_PATH)/rootfs/usr/lib -lblkid -luuid
else
LDADD_common += -lblkid -luuid
endif
❺ 如何手动创建一个设备节点,写出主要命令及参数
Linux下生成驱动设备节点文件的方法有3个:1、手动mknod;2、利用devfs;3、利用udev
在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点。
在2.6.17以前,在/dev目录下生成设备文件很容易,
devfs_mk_bdev
devfs_mk_cdev
devfs_mk_symlink
devfs_mk_dir
devfs_remove
这几个是纯devfs的api,2.6.17以前可用,但后来devfs被sysfs+udev的形式取代,同时期sysfs文件系统可以用的api:
class_device_create_file,在2.6.26以后也不行了,现在,使用的是device_create ,从2.6.18开始可用
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, const char *fmt, ...)
从2.6.26起又多了一个参数drvdata: the data to be added to the device for callbacks
不会用可以给此参数赋NULL
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
下面着重讲解第三种方法udev
在驱动用加入对udev的支持主要做的就是:在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)( 在2.6较早的内核中用class_device_create)创建对应的设备。
内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
struct class和class_create(…) 以及device_create(…)都包含在在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。
struct class定义在头文件include/linux/device.h中
class_create(…)在/drivers/base/class.c中实现
device_create(…)函数在/drivers/base/core.c中实现
class_destroy(...),device_destroy(...)也在/drivers/base/core.c中实现调用过程类似如下:
static struct class *spidev_class;
/*-------------------------------------------------------------------------*/
static int __devinit spidev_probe(struct spi_device *spi)
{
....
dev =device_create(spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->master->bus_num, spi->chip_select);
...
}
static int __devexit spidev_remove(struct spi_device *spi)
{
......
device_destroy(spidev_class, spidev->devt);
.....
return 0;
}
static struct spi_driver spidev_spi = {
.driver = {
.name = "spidev",
.owner = THIS_MODULE,
},
.probe = spidev_probe,
.remove = __devexit_p(spidev_remove),
};
/*-------------------------------------------------------------------------*/
static int __init spidev_init(void)
{
....
spidev_class =class_create(THIS_MODULE, "spidev");
if (IS_ERR(spidev_class)) {
unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
return PTR_ERR(spidev_class);
}
....
}
mole_init(spidev_init);
static void __exit spidev_exit(void)
{
......
class_destroy(spidev_class);
......
}
mole_exit(spidev_exit);
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
下面以一个简单字符设备驱动来展示如何使用这几个函数
#include <linux/mole.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
int HELLO_MAJOR = 0;
int HELLO_MINOR = 0;
int NUMBER_OF_DEVICES = 2;
struct class *my_class;
//struct cdev cdev;
//dev_t devno;
struct hello_dev {
struct device *dev;
dev_t chrdev;
struct cdev cdev;
};
static struct hello_dev *my_hello_dev = NULL;
struct file_operations hello_fops = {
.owner = THIS_MODULE
};
static int __init hello_init (void)
{
int err = 0;
struct device *dev;
my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);
if (NULL == my_hello_dev) {
printk("%s kzalloc failed!\n",__func__);
return -ENOMEM;
}
devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);
if (HELLO_MAJOR)
err= register_chrdev_region(my_hello_dev->chrdev, 2, "memdev");
else
{
err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, "memdev");
HELLO_MAJOR = MAJOR(devno);
}
if (err) {
printk("%s alloc_chrdev_region failed!\n",__func__);
goto alloc_chrdev_err;
}
printk("MAJOR IS %d\n",HELLO_MAJOR);
cdev_init(&(my_hello_dev->cdev), &hello_fops);
my_hello_dev->cdev.owner = THIS_MODULE;
err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);
if (err) {
printk("%s cdev_add failed!\n",__func__);
goto cdev_add_err;
}
printk (KERN_INFO "Character driver Registered\n");
my_class =class_create(THIS_MODULE,"hello_char_class"); //类名为hello_char_class
if(IS_ERR(my_class))
{
err = PTR_ERR(my_class);
printk("%s class_create failed!\n",__func__);
goto class_err;
}
dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,"memdev%d",0); //设备名为memdev
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
gyro_err("%s device_create failed!\n",__func__);
goto device_err;
}
printk("hello mole initialization\n");
return 0;
device_err:
device_destroy(my_class, my_hello_dev->chrdev);
class_err:
cdev_del(my_hello_dev->chrdev);
cdev_add_err:
unregister_chrdev_region(my_hello_dev->chrdev, 1);
alloc_chrdev_err:
kfree(my_hello_dev);
return err;
}
static void __exit hello_exit (void)
{
cdev_del (&(my_hello_dev->cdev));
unregister_chrdev_region (my_hello_dev->chrdev,1);
device_destroy(my_class, devno); //delete device node under /dev//必须先删除设备,再删除class类
class_destroy(my_class); //delete class created by us
printk (KERN_INFO "char driver cleaned up\n");
}
mole_init (hello_init);
mole_exit (hello_exit);
MODULE_LICENSE ("GPL");
这样,模块加载后,就能在/dev目录下找到memdev这个设备节点了。
例2:内核中的drivers/i2c/i2c-dev.c
在i2cdev_attach_adapter中调用device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
这样在dev目录就产生i2c-0 或i2c-1节点
接下来就是udev应用,udev是应用层的东西,udev需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间
udev的源码可以在去相关网站下载,然后就是对其在运行环境下的移植,指定交叉编译环境,修改Makefile下的CROSS_COMPILE,如为mipsel-linux-,DESTDIR=xxx,或直接make CROSS_COMPILE=mipsel-linux-,DESTDIR=xxx 并install
把主要生成的udevd、udevstart拷贝rootfs下的/sbin/目录内,udev的配置文件udev.conf和rules.d下的rules文件拷贝到rootfs下的/etc/目录内
并在rootfs/etc/init.d/rcS中添加以下几行:
echo “Starting udevd...”
/sbin/udevd --daemon
/sbin/udevstart
(原rcS内容如下:
# mount filesystems
/bin/mount -t proc /proc /proc
/bin/mount -t sysfs sysfs /sys
/bin/mount -t tmpfs tmpfs /dev
# create necessary devices
/bin/mknod /dev/null c 1 3
/bin/mkdir /dev/pts
/bin/mount -t devpts devpts /dev/pts
/bin/mknod /dev/audio c 14 4
/bin/mknod /dev/ts c 10 16
)
这样当系统启动后,udevd和udevstart就会解析配置文件,并自动在/dev下创建设备节点文件