嵌入式Linux字符设备驱动开发流程——以LED为例
前言
留空
头文件
#include //字符设备描述
#include //内存分配头文件
内存申请kmalloc
文件(路径):未知
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
if (__builtin_constant_p(size)) {
if (size > KMALLOC_MAX_CACHE_SIZE)
return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
if (!(flags & GFP_DMA)) {
int index = kmalloc_index(size);
if (!index)
return ZERO_SIZE_PTR;
return kmem_cache_alloc_trace(kmalloc_caches[index],
flags, size);
}
#endif
}
}
字符设备结构体
所属文件名:cdev.h
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
字符设备初始化
所属文件名:未知
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
字符类设备注册
所属文件名:未知
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
字符设备卸载
所属文件名:未知
/**
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
*/
void cdev_del(struct cdev *p)
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}
内存释放
所属文件名:未知
void kfree(const void *x)
{
struct page *page;
void *object = (void *)x;
trace_kfree(_RET_IP_, x);
if (unlikely(ZERO_OR_NULL_PTR(x)))
return;
page = virt_to_head_page(x);
if (unlikely(!PageSlab(page))) {
BUG_ON(!PageCompound(page));
kmemleak_free(x);
put_page(page);
return;
}
slab_free(page->slab, page, object, _RET_IP_);
}
程序
#include //模块头文件
#include //内核头文件
#include //内核初始化
#include //字符设备函数
#include //字符设备描述
#include //系列设备号处理宏
#include //内存分配头文件
#define DEVICE_NAME "leds" //字符设备名称
#define DEVICE_MINOR_NUM 2 //字符设备数量
#define DEV_MAJOR 0 //主设备号
#define DEV_MINOR 0 //次设备号,0为自动分配
#define REGDEV_SIZE 3000
static int leds_major = DEV_MAJOR; //主设备号变量
static int leds_minor = DEV_MINOR; //次设备号变量
static dev_t leds_dev; //设备号
struct cdev *leds_cdev; //字符设备结构体变量
struct file_operations leds_fops = {
.owner = THIS_MODULE,
};
static __init int leds_init(void)
{
int ret = 0;
int i;
ret = alloc_chrdev_region(&leds_dev, leds_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
if(ret < 0){
printk(KERN_EMERG "register_chrdev_region req %d is failed!\n", DEV_MAJOR);
return ret;
}
leds_major = MAJOR(leds_dev); //主设备号
leds_minor = MINOR(leds_dev); //次设备号
printk(KERN_EMERG "leds chrdev major=%d, minor=%d\n", leds_major, leds_minor);
leds_cdev = kmalloc(DEVICE_MINOR_NUM * sizeof(struct cdev), GFP_KERNEL);
if(leds_cdev == NULL)
{
printk(KERN_EMERG "kmalloc failed");
unregister_chrdev_region(leds_dev, DEVICE_MINOR_NUM);
return -ENOMEM;
}
//memset(leds_cdev, 0, DEVICE_MINOR_NUM * sizeof(struct dev_cdev));
for(i=0; i
cdev_init(&leds_cdev[i], &leds_fops);
leds_cdev[i].owner = THIS_MODULE;
leds_cdev[i].ops = &leds_fops;
ret = cdev_add(&leds_cdev[i], MKDEV(leds_major, leds_minor+1), 1);
if(ret < 0){
printk(KERN_EMERG "cdev_add %d failed!\n", i);
}
else{
printk(KERN_EMERG "cdev_add %d success!\n", i);
}
}
return ret;
}
static __exit void leds_exit(void)
{
int i;
for(i=0; i
cdev_del(&leds_cdev[i]); //注销设备
}
kfree(leds_cdev); //释放内存
unregister_chrdev_region(leds_dev, DEVICE_MINOR_NUM); //注销设备号
printk(KERN_EMERG "leds chrdev exit \n");
}
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
结束语
以上则为嵌入式Linux注册字符类设备的相关内容。
~谢谢支持~
如果文章对您有帮助,欢迎移至上方按钮打赏博主;