一、字符設備註冊流程
1、申請設備號
申請函數:
register_chrdev_region() 用於已知設備號的情況
alloc_chrdev_region() 用於未知設備號的情況,向系統申請未被佔用的設備號
2、註冊設備
3、創建設備節點
mknod /dev/NAME 設備類型 major minor
示例1
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define SECOND_MAJOR 248 /*預設的second的主設備號*/
static int second_major = SECOND_MAJOR;
/*second設備結構體*/
struct second_dev {
struct cdev cdev; /*cdev結構體*/
atomic_t counter;/* 一共經歷了多少秒?*/
struct timer_list s_timer; /*設備要使用的定時器*/
};
struct second_dev *second_devp; /*設備結構體指針*/
/*定時器處理函數*/
static void second_timer_handle(unsigned long arg)
{
mod_timer(&second_devp->s_timer,jiffies + HZ);
atomic_inc(&second_devp->counter);
printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
}
/*文件打開函數*/
int second_open(struct inode *inode, struct file *filp)
{
/*初始化定時器*/
init_timer(&second_devp->s_timer);
second_devp->s_timer.function = &second_timer_handle;
second_devp->s_timer.expires = jiffies + HZ;
add_timer(&second_devp->s_timer); /*添加(註冊)定時器*/
atomic_set(&second_devp->counter,0); //計數清0
return 0;
}
/*文件釋放函數*/
int second_release(struct inode *inode, struct file *filp)
{
del_timer(&second_devp->s_timer);
return 0;
}
/*讀函數*/
static ssize_t second_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
int counter;
counter = atomic_read(&second_devp->counter);
if(put_user(counter, (int*)buf))
return - EFAULT;
else
return sizeof(unsigned int);
}
/*文件操作結構體*/
static const struct file_operations second_fops = {
.owner = THIS_MODULE,
.open = second_open,
.release = second_release,
.read = second_read,
};
/*初始化並註冊cdev*/
static void second_setup_cdev(struct second_dev *dev, int index)
{
int err, devno = MKDEV(second_major, index);
cdev_init(&dev->cdev, &second_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
/*設備驅動模塊加載函數*/
int second_init(void)
{
int ret;
dev_t devno = MKDEV(second_major, 0);
/* 申請設備號*/
if (second_major)
ret = register_chrdev_region(devno, 1, "second");
else { /* 動態申請設備號 */
ret = alloc_chrdev_region(&devno, 0, 1, "second");
second_major = MAJOR(devno);
}
if (ret < 0)
return ret;
/* 動態申請設備結構體的內存*/
second_devp = kmalloc(sizeof(struct second_dev), GFP_KERNEL);
if (!second_devp) { /*申請失敗*/
ret = - ENOMEM;
goto fail_malloc;
}
memset(second_devp, 0, sizeof(struct second_dev));
second_setup_cdev(second_devp, 0);
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return ret;
}
/*模塊卸載函數*/
void second_exit(void)
{
cdev_del(&second_devp->cdev); /*註銷cdev*/
kfree(second_devp); /*釋放設備結構體內存*/
unregister_chrdev_region(MKDEV(second_major, 0), 1); /*釋放設備號*/
}
MODULE_AUTHOR("Barry Song <[email protected]>");
MODULE_LICENSE("Dual BSD/GPL");
module_param(second_major, int, S_IRUGO);
module_init(second_init);
module_exit(second_exit);
示例一需要手動創建 設備節點 、
示例2:
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#define DEVICE_NAME "zhanli" /* 加載模式後,執行”cat /proc/devices”命令看到的設備名稱 */
#define LED_MAJOR 231 /* 主設備號 */
static int file_open(struct inode *inode, struct file *file)
{
return 0;
}
static int file_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}
static struct file_operations EmbedSky_leds_fops =
{
.owner = THIS_MODULE,
.open = file_open,
.unlocked_ioctl = file_ioctl,
};
static char __initdata banner[] = "TQ2440/SKY2440 LEDS, (c) 2008,2009 www.embedsky.net\n";
static struct class *led_class;
static int __init init_dev(void)
{
int ret;
printk(banner);
/* 註冊字符設備驅動程序
* 參數爲主設備號、設備名字、file_operations 結構;
* 這樣,主設備號就和具體的 file_operations 結構聯繫起來了,
* 操作主設備爲 LED_MAJOR 的設備文件時,就會調用 EmbedSky_leds_fops 中的相關成員函數
* LED_MAJOR 可以設爲 0,表示由內核自動分配主設備號
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &EmbedSky_leds_fops);
if (ret < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
//註冊一個類,使 mdev 可以在"/dev/"目錄下面建立設備節點
led_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(led_class))
{
printk("Err: failed in EmbedSky-leds class. \n");
return -1;
}
//創建一個設備節點,節點名爲 DEVICE_NAME
device_create(led_class, NULL, MKDEV(LED_MAJOR, 0), NULL, DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
return 0;
}
static void __exit exit_dev(void)
{
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
device_destroy(led_class, MKDEV(LED_MAJOR, 0)); //刪掉設備節點
class_destroy(led_class); //註銷類
}
module_init(init_dev);
module_exit(exit_dev);
MODULE_AUTHOR("zl");
MODULE_LISENCE("GPL");