字元驅動小例子解析

公子燁發表於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函式。

相關文章