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

Linux驱动开发入门(三)

在上一篇文章中,我们制作了一个char设备,但是它是只读的。在这一篇文章中,我们做一个可读写的char设备:

/* * http://linux.die.net/lkmpg/x569.html * http://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/ *//* *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 "cdev_rw"/* 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 my_data;/* 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);printk(KERN_INFO "Unregistering char device");}/* * 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){if (Device_Open)return -EBUSY;Device_Open++;msg_Ptr = my_data;try_module_get(THIS_MODULE);printk(KERN_INFO "cdev->device_open");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);printk(KERN_INFO "cdev->device_release");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++;}printk(KERN_INFO "cdev->device_read: %s", my_data);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){short ind = len-1;short count=0;memset(my_data,0,100);while(len>0){my_data = buff; //copy the given string to the driver but in reverselen--;}      printk(KERN_INFO "cdev->device_write: %s", my_data);return count;}

然后制作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_rw.oelseKERNELDIR ?= /lib/modules/$(shell uname -r)/buildPWD       := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules #LDDINCDIR=$(PWD)/../include modulesendifuninstall:-rmmod chardev_rw.ko-rm /dev/cdev_rw*reinstall: uninstall clean default-insmod chardev_rw.koclean:rm -rf *.order *.symvers *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versionsdepend .depend dep:$(CC) $(CFLAGS) -M *.c > .dependifeq (.depend,$(wildcard .depend))include .dependendif.PHONY: uninstall clean default

接下来是编译并安装这个设备:

http://dl.iteye.com/upload/attachment/0069/3829/e62fd728-dce5-3ab1-b54c-e534e70e18ee.png

查看一下设备输出的日志:

http://dl.iteye.com/upload/attachment/0069/3831/82396cd8-e0ad-3baa-9e77-0b8b5057290f.png

挂装这个设备:

http://dl.iteye.com/upload/attachment/0069/3833/0c4dacf1-21e2-3a6f-9aee-1279cd129da7.png

为了向这个设备写入内容,我们要变更一下设备文件的权限,让所有人可读写:

http://dl.iteye.com/upload/attachment/0069/3835/75697d6e-c6ad-3a1d-a6c4-0d297f1e76eb.png

最后我们使用一下这个设备试试:

http://dl.iteye.com/upload/attachment/0069/3839/a1e0a726-68c7-367a-8113-36d30b696c82.png
页: [1]
查看完整版本: Linux驱动开发入门(三)