添加一个普通的字符设备一般需要如下一些步骤:
1. 字符设备声明:
static struct cdev *pcdev;
static dev_t ndev;
静态的声明了 一个字符设备 cdev, 和设备号 dev_t;
2. 模块加载过程中,添加一个字符设备:
static int demo_map_init(void)
{
int err = 0;
char *kstr;
...
//动态申请一个 字符设备内存。
pcdev = cdev_alloc();
//字符设备初始化
cdev_init(pcdev, &mmap_fops);
//动态注册一个字符设备
alloc_chrdev_region(&ndev, 0, 1, "mmap_dev");
printk("major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev));
pcdev->owner = THIS_MODULE;
//向系统中 添加一个字符设备
cdev_add(pcdev, ndev, 1);
...
}
struct cdev *cdev_alloc(void) --- 动态申请一块字符设备内存
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
void cdev_init(struct cdev *cdev, const struct file_operations *fops) --- 初始化一个字符设备
/**
* 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;
}
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) ------动态注册字符设备
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}
int cdev_add(struct cdev *p, dev_t dev, unsigned count) ---想系统中添加字符设备
/**
* 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);
}
3. 卸载字符设备:
static void demo_map_exit(void)
{
del_timer_sync(&timer);
// 从系统中删除字符设备
cdev_del(pcdev);
//注销字符设备
unregister_chrdev_region(ndev, 1);
kunmap(pg);
ClearPageReserved(pg);
__free_pages(pg, 0);
}
void cdev_del(struct cdev *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 unregister_chrdev_region(dev_t from, unsigned count) --- 注销count 个字符设备
/**
* unregister_chrdev_region() - return a range of device numbers
* @from: the first in the range of numbers to unregister
* @count: the number of device numbers to unregister
*
* This function will unregister a range of @count device numbers,
* starting with @from. The caller should normally be the one who
* allocated those numbers in the first place...
*/
void unregister_chrdev_region(dev_t from, unsigned count)
{
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
}