㈠ 如何手动创建一个设备节点,写出主要命令及参数
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下创建设备节点文件
㈡ arm linux驱动程序和应用程序之间的一些问题
我不知道你用的那个版本的kernel,但是据我所知,ioctl的cmd参数的定义,不是你那么简单的....
需要用到ioctl的命令字定义的几个宏定义...
自己定义是没有用的,因为ioctl需要知道你的命令是io读还是io写..
比如在我的程序里面我这样定义命令字...
你自己查一下_IOW,_IO这两个宏...
#include <linux/ioctl.h>//使用下面的宏
#define DEVICE_NAME "/dev/test_device"
#define TEST_MAGIC_NUM 'k'
#define PORT_SET _IOW(TEST_MAGIC_NUM,1,int)
#define PORT_GET _IO(TEST_MAGIC_NUM,2)
#define PORT_LOCK _IO(TEST_MAGIC_NUM,3)
你的应用程序中,也应该把这样定义的命令字给包含进去...这两个个宏的意义是,不要和系统已经使用ioctl的cmd重复...因为ioctl接口并不是仅仅给你使用的.你自己写的命令字会把系统原有的给覆盖...
㈢ Linux下codeblocks如何调用Linux中的设备注册函数例如cdev_add()函数
struct cdev *my_cdev = cdev_alloc( );
my_cdev->ops = &my_fops;
void cdev_init(struct cdev *cdev, struct file_operations *fops);
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
㈣ 写linux驱动的请帮我看看,我这个程序怎么创建不了设备文件
参考答案 智者不惑,仁者不忧,勇者不惧。--孔子
㈤ 字符驱动设备中几个重要的结构体(cdev,file
1. cdev结构体
[cpp]view plain
structcdev{
structkobjectkobj;//内嵌的kobject对象
structmole*owner;//所属模板
conststructfile_operations*ops;//文件操作的结构体
structlist_headlist;
dev_tdev;//设备号
unsignedintcount;
};
1.1 cdev的相关操作
[cpp]view plain
voidcdev_init(structcdev*,conststructfile_operations*);//初始化使其和文件操作结构相连接
structcdev橡冲巧*cdev_alloc(void);//为cdev分配内存
intcdev_add(structcdev*,dev_t,unsigned);//向内核注册一个设备
voidcdev_del(structcdev*);//从内梁键核删除一个设备
1.2 设备号的分配
1.21 主次设备号和dev_t的相互转换
由dev_t号获取主次设备号
[cpp]view plain
MAJOR(dev_tdev)
MINOR(dev_tdev)
由主次设备号获得dev_t
[cpp]view plain
MKDEV(intmajor,intminor)
1.22 获取及注销设备号
[cpp]view plain
<spanstyle="font-size:14px;">intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name);//手动注册
intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name);//由内核分配
voinregion_chrdev_region(dev_tfrom,unsignedcount);//注销设判纳备号</span>
㈥ cdev结构代表着什么
代表字符设备
㈦ 两个注册设备驱动的函数有什么区别
register_chrdev的作用是向内核申请分配一个单独渣脊核主设备号和范围在 0 ~ 255 的次设如掘备号,野笑如果申请的主设备号为 0 则内核会动态分配一个;cdev_add是向内核添加一个cdev结构;完全两码事。 查看原帖>>
㈧ android gpio 怎么分配
驱动程序初始化和退出
static int simple_major = 250;//默认的设备号码,如果为0则尝试自动分配
……
/*
* Set up the cdev structure for a device.
*/
static void simple_setup_cdev(struct cdev *dev, int minor,
struct file_operations *fops)//自编的函高烂数,注册字符设备
{
int err, devno = MKDEV(simple_major, minor);//建立设备号
cdev_init(dev, fops);//初始化设备结构体struct cdev *dev
dev->owner = THIS_MODULE;
dev->ops = fops;//关衫野联fops
err = cdev_add (dev, devno, 1);//注册一个字符设备
/* Fail gracefully if need be */
if (err)//注册失败处理
printk (KERN_NOTICE "Error %d adding simple%d", err, minor);
}
/*
* Our various sub-devices.
*/
/* Device 0 uses remap_pfn_range */
static struct file_operations simple_remap_ops = { //定义设备的fops
.owner = THIS_MODULE,
.open = simple_open,
.release = simple_release,
.read = simple_read,
.write = simple_write,
.ioctl = simple_ioctl,
};/*
* We export two simple devices. There's no need for us to maintain any
* special housekeeping info, so we just deal with raw cdevs.
*/
static struct cdev SimpleDevs;/*
* Mole housekeeping.
*/
static struct class *my_class;
static int simple_init(void)
{
int result;
dev_t dev = MKDEV(simple_major, 0);//将设备号转化为dev_t的结构
/* Figure out our device number. */
if (simple_major)
result = register_chrdev_region(dev, 1, "simple");//尝试申请主设备号
else {
result = alloc_chrdev_region(&dev, 0, 1, "simple");//请求自动分配主设备号,起始值是0,总共分配1个或念喊,设备名simple
simple_major = MAJOR(dev);//将分配成功的设备号保存在simple_major变量中
}
if (result < 0) {//分配主设备号失败
printk(KERN_WARNING "simple: unable to get major %d\n", simple_major);
return result;
}
if (simple_major == 0)//将返回值记录为主设备号。需要么?
simple_major = result;
/* Now set up two cdevs. */
simple_setup_cdev(&SimpleDevs, 0, &simple_remap_ops);//调用自编的函数注册字符设备,有Bug没有返回注册是否成功。
printk("simple device installed, with major %d\n", simple_major);//Bug:打印前应该检查注册是否成功?
my_class= class_create(THIS_MODULE, "simple");//建立一个叫simple的内核class,目的是下一步创建设备节点文件
device_create(my_class, NULL, MKDEV(simple_major, 0),
NULL, "led");//创建设备节点文件
return 0;
}
static void simple_cleanup(void)
{
cdev_del(&SimpleDevs);//删除字符设备
unregister_chrdev_region(MKDEV(simple_major, 0), 1);//注销主设备号
device_destroy(my_class,MKDEV(simple_major,0));//删除设备节点
printk("simple device uninstalled\n");
}mole_init(simple_init);
mole_exit(simple_cleanup);
㈨ 嵌入式linux设备驱动,无法打开设备文件
1. ls /dev/* 看看有没有你的LED节点
2.cat /proc/devices 看看有没有相关LED驱动信息。
===============================
static const struct file_operations fops_led =
{
.owner = THIS_MODULE,
//.open = open_led,
.unlocked_ioctl = unlocked_ioctl_led,
};
都屏蔽了open函数,内怎么打开容?
㈩ 请问linux2.6内核驱动程序的自动创建设备节点的 class_create device_create 创建设备问题。
是这么回事,当你自己要写一个字符设备或者看别人写的是字符设备时内,要定义一个字符容设备的结构体struct cdev{/*里面是一些字符设备的相关属性,包括file_operations结构体,设备号等等*/},然后调用register_chrdev_region(),申请设备号,再用cdev_add()想内核注册设备,这里,内核就知道你要注册的就是字符设备了,同理,如果是块设备的话用register_blkdev()来注册块设备,经过一系列的初始化后添加add_disk(),内核也就知道你添加的是块设备了