阿男bluedash 发表于 2013-1-26 12:29:18

Linux驱动开发入门(二)

本文制作一个char设备。撰写chardev.c如下:

/* * http://linux.die.net/lkmpg/x569.html *//* *chardev.c: Creates a read-only char device that says how many times *you've read from the dev file */#include <linux/kernel.h>#include <linux/module.h>#include <linux/fs.h>#include <asm/uaccess.h>      /* for put_user *//*   *Prototypes - this would normally go in a .h file */int init_module(void);void cleanup_module(void);static int device_open(struct inode *, struct file *);static int device_release(struct inode *, struct file *);static ssize_t device_read(struct file *, char *, size_t, loff_t *);static ssize_t device_write(struct file *, const char *, size_t, loff_t *);#define SUCCESS 0#define DEVICE_NAME "chardev"   /* Dev name as it appears in /proc/devices   */#define BUF_LEN 80            /* Max length of the message from the device *//** Global variables are declared as static, so are global within the file.*/static int Major;               /* Major number assigned to our device driver */static int Device_Open = 0;   /* Is device open?                                 * Used to prevent multiple access to device */static char msg;       /* The msg the device will give when asked */static char *msg_Ptr;static struct file_operations fops = {      .read = device_read,      .write = device_write,      .open = device_open,      .release = device_release};/* * This function is called when the module is loaded */int init_module(void){      Major = register_chrdev(0, DEVICE_NAME, &fops);      if (Major < 0) {          printk(KERN_ALERT "Registering char device failed with %d\n", Major);          return Major;      }      printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);      printk(KERN_INFO "the driver, create a dev file with\n");      printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);      printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");      printk(KERN_INFO "the device file.\n");      printk(KERN_INFO "Remove the device file and module when done.\n");      return SUCCESS;}/* * This function is called when the module is unloaded */void cleanup_module(void){      /*          * Unregister the device          */      unregister_chrdev(Major, DEVICE_NAME);}/* * Methods *//** Called when a process tries to open the device file, like * "cat /dev/mycharfile" */static int device_open(struct inode *inode, struct file *file){      static int counter = 0;      if (Device_Open)                return -EBUSY;      Device_Open++;      sprintf(msg, "I already told you %d times Hello world!\n", counter++);      msg_Ptr = msg;      try_module_get(THIS_MODULE);      return SUCCESS;}/** Called when a process closes the device file. */static int device_release(struct inode *inode, struct file *file){      Device_Open--;          /* We're now ready for our next caller */      /*          * Decrement the usage count, or else once you opened the file, you'll         * never get get rid of the module.          */      module_put(THIS_MODULE);      return 0;}/** Called when a process, which already opened the dev file, attempts to * read from it. */static ssize_t device_read(struct file *filp,   /* see include/linux/fs.h   */                           char *buffer,      /* buffer to fill with data */                           size_t length,       /* length of the buffer   */                           loff_t * offset){      /*         * Number of bytes actually written to the buffer          */      int bytes_read = 0;      /*         * If we're at the end of the message,          * return 0 signifying end of file          */      if (*msg_Ptr == 0)                return 0;      /*          * Actually put the data into the buffer          */      while (length && *msg_Ptr) {                /*                  * The buffer is in the user data segment, not the kernel                  * segment so "*" assignment won't work.We have to use                  * put_user which copies data from the kernel data segment to               * the user data segment.                  */                put_user(*(msg_Ptr++), buffer++);                length--;                bytes_read++;      }      /*          * Most read functions return the number of bytes put into the buffer         */      return bytes_read;}/*   * Called when a process writes to dev file: echo "hi" > /dev/hello*/static ssize_tdevice_write(struct file *filp, const char *buff, size_t len, loff_t * off){      printk(KERN_ALERT "Sorry, this operation isn't supported.\n");      return -EINVAL;}

然后撰写Makefile:

# Comment/uncomment the following line to disable/enable debugging#DEBUG = y# Add your debugging flag (or not) to CFLAGSifeq ($(DEBUG),y)DEBFLAGS = -O -g # "-O" is needed to expand inlineselseDEBFLAGS = -O2endifEXTRA_CFLAGS += $(DEBFLAGS) #-I$(LDDINCDIR)ifneq ($(KERNELRELEASE),)# call from kernel build systemobj-m   := chardev.oelseKERNELDIR ?= /lib/modules/$(shell uname -r)/buildPWD       := $(shell pwd)default:      $(MAKE) -C $(KERNELDIR) M=$(PWD) modules #LDDINCDIR=$(PWD)/../include modulesendifclean:      rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versionsdepend .depend dep:      $(CC) $(CFLAGS) -M *.c > .dependifeq (.depend,$(wildcard .depend))include .dependendif

接下来是生成目标文件:

http://dl.iteye.com/upload/attachment/0069/3529/f0a20418-fd5b-31ff-8ee1-8fa1bc3cecb6.png

然后是安装驱动,并查看内核的日志输出:

http://dl.iteye.com/upload/attachment/0069/3533/6f4988f8-5123-34ff-80a6-3865c7c6661e.png

看到驱动已经成功加载了,此时查看设备列表:

http://dl.iteye.com/upload/attachment/0069/3535/d3e742df-6681-3521-acbc-925f9a345b86.png

可以看到最后一行,设备已经加载了。此时使用mknod在dev中安装这个chardev设备:

http://dl.iteye.com/upload/attachment/0069/3537/0cda9749-1eaa-3778-8285-fb8050cfb5f9.png

此时已经可以使用这个设备了:

http://dl.iteye.com/upload/attachment/0069/3539/f9f767e3-b514-3b45-8250-ea5156462b14.png

测试完成后,删除设备并卸载驱动:

http://dl.iteye.com/upload/attachment/0069/3541/e21b6aa8-7d70-310b-b11f-ac32a205008d.png
页: [1]
查看完整版本: Linux驱动开发入门(二)