字元驅動小例子解析
公子燁發表於2011-10-08
1.
MODULE_LICENSE ("GPL");
int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;
char data[50]="foobar not equal to barfoo";
struct cdev cdev;
dev_t dev = 0;
static int hello_open (struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hey! device opened\n");
return 0;
}
static int hello_release (struct inode *inode, struct file *file)
{
printk (KERN_INFO "Hmmm... device closed\n");
return 0;
}
ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
ssize_t result = 0;
if (copy_to_user (buff, data, sizeof(data)-1)) // copy_to_user將資料返回給使用者
result = -EFAULT;
else
printk (KERN_INFO "wrote %d bytes\n", count);
return result;
}
ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
ssize_t ret = 0;
printk (KERN_INFO "Writing %d bytes\n", count);
if (count>127) return -ENOMEM;
if (count<0) return -EINVAL;
if (copy_from_user (data, buf, count)) { // copy_from_user將使用者資料讀到本地buffer
ret = -EFAULT;
}
else {
data[127]='\0';
printk (KERN_INFO"Received: %s\n", data);
ret = count;
}
return ret;
}
struct file_operations hello_fops = { //檔案操作
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write
};
static int __init hello_2_init (void)
{
int result;
dev = MKDEV (hello_major, hello_minor); //拼裝裝置號,高12位為主裝置號,低20位為從裝置號。include/linux/kdev_t.h中定義的巨集MKDEV。
result = register_chrdev_region (dev, number_of_devices, "hello");// fs/char_dev.c中定義的函式,註冊一個範圍內的裝置號,成功返回0。
if (result<0) {
printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
return result;
}
/* dynamic allocation */
cdev = cdev_alloc(); // fs/char_dev.c中定義的函式,分配一個cdev結構體
cdev_init (cdev, &hello_fops); // fs/char_dev.c中定義的函式,關聯cdev結構體和file_operations(在linux/fs.h中定義的結構體,裡面為檔案的常用操作定義)。
cdev->owner = THIS_MODULE;
// cdev->ops = &hello_fops;
result = cdev_add (cdev, dev, 1); // fs/char_dev.c中定義的函式,關聯cdev結構體和dev(裝置號)。
if (result)
printk (KERN_INFO "Error %d adding char_device", result);
printk (KERN_INFO "Char device registered ...\n");
return 0;
}
static void __exit hello_2_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
if (cdev)
cdev_del (cdev); // fs/char_dev.c中定義的函式,取消cdev結構體和dev(裝置號)的關聯
unregister_chrdev_region (devno, number_of_devices); // fs/char_dev.c中定義的函式,釋放cdev結構體的空間和dev裝置號
printk (KERN_INFO "char device cleaned up :)\n");
}
module_init (hello_2_init);
module_exit (hello_2_exit); //巨集module_init和module_exit都被定義在linux原始碼的include/linux/init.h中,是為init_module和cleanup_module起別名用的(在靜態編譯時防止重複定義init_module和cleanup_module)。
2.
當open一個裝置節點時,inode中德i_cdev會指向找到的cdev,同時file中的f_op獲得已經註冊的驅動的file_operations。
cdev---dev (如:250,0)
---ops(mypos)---open,read,write等。<----------------------> file---f_op
3.
使用者呼叫read等系統呼叫時,系統首先呼叫sys_read(SYSCALL_DEFINE3),再呼叫vfs_read,再呼叫初始化完成的結構體file中的file_operations中的read函式,即為呼叫了hello_read函式。