⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chardev.c

📁 对linux内核编程做了详细的解析和举例
💻 C
字号:
/* chardev.c  * Copyright (C) 1998-1999 by Ori Pomerantz *  * Create a character device (read only) *//* The necessary header files *//* Standard in kernel modules */#include <linux/kernel.h>   /* We're doing kernel work */#include <linux/module.h>   /* Specifically, a module *//* Deal with CONFIG_MODVERSIONS */#if CONFIG_MODVERSIONS==1#define MODVERSIONS#include <linux/modversions.h>#endif        /* For character devices */#include <linux/fs.h>       /* The character device                              * definitions are here */#include <linux/wrapper.h>  /* A wrapper which does                              * next to nothing at                             * at present, but may                              * help for compatibility                             * with future versions                              * of Linux *//* In 2.2.3 /usr/include/linux/version.h includes  * a macro for this, but 2.0.35 doesn't - so I add  * it here if necessary. */#ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))#endif/* Conditional compilation. LINUX_VERSION_CODE is  * the code (as per KERNEL_VERSION) of this version. */#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)#include <asm/uaccess.h>  /* for put_user */#endif			     #define SUCCESS 0/* Device Declarations **************************** *//* The name for our device, as it will appear  * in /proc/devices */#define DEVICE_NAME "char_dev"/* The maximum length of the message from the device */#define BUF_LEN 80/* Is the device open right now? Used to prevent  * concurent access into the same device */static int Device_Open = 0;/* The message the device will give when asked */static char Message[BUF_LEN];/* How far did the process reading the message  * get? Useful if the message is larger than the size  * of the buffer we get to fill in device_read. */static char *Message_Ptr;/* This function is called whenever a process  * attempts to open the device file */static int device_open(struct inode *inode, 		       struct file *file){  static int counter = 0;#ifdef DEBUG  printk ("device_open(%p,%p)\n", inode, file);#endif  /* This is how you get the minor device number in    * case you have more than one physical device using    * the driver. */  printk("Device: %d.%d\n", 	 inode->i_rdev >> 8, inode->i_rdev & 0xFF);  /* We don't want to talk to two processes at the    * same time */  if (Device_Open)    return -EBUSY;  /* If this was a process, we would have had to be    * more careful here.   *   * In the case of processes, the danger would be    * that one process might have check Device_Open    * and then be replaced by the schedualer by another    * process which runs this function. Then, when the    * first process was back on the CPU, it would assume    * the device is still not open.    *   * However, Linux guarantees that a process won't be    * replaced while it is running in kernel context.    *   * In the case of SMP, one CPU might increment    * Device_Open while another CPU is here, right after    * the check. However, in version 2.0 of the    * kernel this is not a problem because there's a lock    * to guarantee only one CPU will be kernel module at    * the same time. This is bad in  terms of    * performance, so version 2.2 changed it.    * Unfortunately, I don't have access to an SMP box    * to check how it works with SMP.   */  Device_Open++;  /* Initialize the message. */  sprintf(Message,     "If I told you once, I told you %d times - %s",    counter++,    "Hello, world\n");  /* The only reason we're allowed to do this sprintf    * is because the maximum length of the message    * (assuming 32 bit integers - up to 10 digits   * with the minus sign) is less than BUF_LEN, which    * is 80. BE CAREFUL NOT TO OVERFLOW BUFFERS,    * ESPECIALLY IN THE KERNEL!!!    */   Message_Ptr = Message;  /* Make sure that the module isn't removed while    * the file is open by incrementing the usage count    * (the number of opened references to the module, if    * it's not zero rmmod will fail)   */  MOD_INC_USE_COUNT;  return SUCCESS;}/* This function is called when a process closes the  * device file. It doesn't have a return value in  * version 2.0.x because it can't fail (you must ALWAYS * be able to close a device). In version 2.2.x it is  * allowed to fail - but we won't let it.  */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)static int device_release(struct inode *inode, 			  struct file *file)#else static void device_release(struct inode *inode, 			   struct file *file)#endif{#ifdef DEBUG  printk ("device_release(%p,%p)\n", inode, file);#endif   /* We're now ready for our next caller */  Device_Open --;  /* Decrement the usage count, otherwise once you    * opened the file you'll never get rid of the module.   */  MOD_DEC_USE_COUNT;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)  return 0;#endif}/* This function is called whenever a process which  * have already opened the device file attempts to  * read from it. */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)static ssize_t device_read(struct file *file,    char *buffer,    /* The buffer to fill with data */    size_t length,   /* The length of the buffer */    loff_t *offset)  /* Our offset in the file */#elsestatic int device_read(struct inode *inode,                       struct file *file,    char *buffer,   /* The buffer to fill with 		     * the data */     int length)     /* The length of the buffer                      * (mustn't write beyond that!) */#endif{  /* Number of bytes actually written to the buffer */  int bytes_read = 0;  /* If we're at the end of the message, return 0    * (which signifies end of file) */  if (*Message_Ptr == 0)    return 0;  /* Actually put the data into the buffer */  while (length && *Message_Ptr)  {    /* Because the buffer is in the user data segment,      * not the kernel data segment, assignment wouldn't      * work. Instead, we have to use put_user which      * copies data from the kernel data segment to the      * user data segment. */    put_user(*(Message_Ptr++), buffer++);    length --;    bytes_read ++;  }#ifdef DEBUG   printk ("Read %d bytes, %d left\n",     bytes_read, length);#endif   /* Read functions are supposed to return the number     * of bytes actually inserted into the buffer */  return bytes_read;}/* This function is called when somebody tries to write  * into our device file - unsupported in this example. */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)static ssize_t device_write(struct file *file,    const char *buffer,    /* The buffer */    size_t length,   /* The length of the buffer */    loff_t *offset)  /* Our offset in the file */#elsestatic int device_write(struct inode *inode,                        struct file *file,                        const char *buffer,                        int length)#endif{  return -EINVAL;}/* Module Declarations ***************************** *//* The major device number for the device. This is  * global (well, static, which in this context is global * within this file) because it has to be accessible  * both for registration and for release. */static int Major;/* This structure will hold the functions to be  * called when a process does something to the device  * we created. Since a pointer to this structure is  * kept in the devices table, it can't be local to * init_module. NULL is for unimplemented functions. */struct file_operations Fops = {  NULL,   /* seek */  device_read,   device_write,  NULL,   /* readdir */  NULL,   /* select */  NULL,   /* ioctl */  NULL,   /* mmap */  device_open,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)  NULL,   /* flush */#endif  device_release  /* a.k.a. close */};/* Initialize the module - Register the character device */int init_module(){  /* Register the character device (atleast try) */  Major = module_register_chrdev(0,                                  DEVICE_NAME,                                 &Fops);  /* Negative values signify an error */  if (Major < 0) {    printk ("%s device failed with %d\n",	    "Sorry, registering the character",	    Major);    return Major;  }  printk ("%s The major device number is %d.\n",          "Registeration is a success.",          Major);  printk ("If you want to talk to the device driver,\n");  printk ("you'll have to create a device file. \n");  printk ("We suggest you use:\n");  printk ("mknod <name> c %d <minor>\n", Major);  printk ("You can try different minor numbers %s",          "and see what happens.\n");  return 0;}/* Cleanup - unregister the appropriate file from /proc */void cleanup_module(){  int ret;  /* Unregister the device */  ret = module_unregister_chrdev(Major, DEVICE_NAME);   /* If there's an error, report it */   if (ret < 0)    printk("Error in unregister_chrdev: %d\n", ret);}  

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -