本文将给出Linux字符设备驱动模版。大家可以根据实际需求修改本模版。
驱动名为hello,内部提供一个简单的buffer,用户可以调用write向设备写入数据,并调用read从设备读出数据。
请注意:1. 若连续调用N次write,buffer只会保留最后一次write的数据。
2. 每次read都将清空buffer。因此,必须先写write设备,再read设备。
3. 目前驱动只能有一个进程访问,允许多个线程访问,并且多个线程共享一个buffer。
对应的测试程序如下:
History:
2014.02.15 单进程,多线程共享buffer版本(2.6.32)。
驱动名为hello,内部提供一个简单的buffer,用户可以调用write向设备写入数据,并调用read从设备读出数据。
请注意:1. 若连续调用N次write,buffer只会保留最后一次write的数据。
2. 每次read都将清空buffer。因此,必须先写write设备,再read设备。
3. 目前驱动只能有一个进程访问,允许多个线程访问,并且多个线程共享一个buffer。
下面的驱动代码适用于内核版本2.6.32,未在其他内核版本上经过测试。
#include
#include
#include
#include
#include
#define DEBUG #include
#include
#include
#include
#include
#define DEVICE_NAME "hello" static unsigned bufsize = 512; module_param(bufsize, uint, S_IRUGO); MODULE_PARM_DESC(bufsize, "data bytes in biggest supported hello buffer"); struct hello_data{ char *name; struct cdev chrdev; struct class *cls; dev_t devt; struct device *dev; atomic_t available; struct mutex mlock; /* Protect @buf */ char *buf; size_t bufsize; size_t actual_len; }; static struct hello_data * hello; static int hello_open(struct inode *inode, struct file *file) { struct hello_data *hello = container_of(inode->i_cdev, struct hello_data, chrdev); /* Only ONE user can open the device. */ if(!atomic_dec_and_test(&hello->available)){ atomic_inc(&hello->available); return -EBUSY; } dev_dbg(hello->dev, "Hello open\n"); if(!strcmp(hello->name, DEVICE_NAME)) dev_dbg(hello->dev, "This is hello\n"); file->private_data = hello; return nonseekable_open(inode, file); } static int hello_close(struct inode *inode, struct file *file) { struct hello_data *hello = file->private_data; dev_dbg(hello->dev, "Hello close\n"); file->private_data = NULL; atomic_inc(&hello->available); return 0; } static int hello_read(struct file *file, char __user *buf, size_t count, loff_t *offp) { struct hello_data *hello = file->private_data; unsigned long ret; u64 len; if(hello->actual_len == 0 || count > hello->actual_len) return -EINVAL; len = min(count, hello->actual_len); mutex_lock(&hello->mlock); ret = copy_to_user(buf, (void *)hello->buf, len); if(ret < 0){ ret = -EFAULT; goto out; } hello->actual_len = 0; ret = len; out: mutex_unlock(&hello->mlock); return ret; } static int hello_write(struct file *file, const char __user *buf, size_t count, loff_t *offp) { struct hello_data *hello = file->private_data; unsigned long ret; u64 len; if(count > hello->bufsize) return -EINVAL; len = min(hello->bufsize, count); mutex_lock(&hello->mlock); ret = copy_from_user(hello->buf, buf, len); if(ret < 0){ ret = -EFAULT; goto out; } hello->actual_len = len; ret = len; out: mutex_unlock(&hello->mlock); return ret; } static struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release= hello_close, .read = hello_read, .write = hello_write, .llseek = no_llseek, }; static int __init hello_init(void) { int ret; hello = kmalloc(sizeof(struct hello_data), GFP_KERNEL); if(hello == NULL){ printk(KERN_ERR "Unable to malloc struct hello_data\n"); return -ENOMEM; } hello->name = DEVICE_NAME; mutex_init(&hello->mlock); atomic_set(&hello->available, 1); hello->buf = kmalloc(bufsize, GFP_KERNEL); if(hello->buf == NULL){ printk(KERN_ERR "Unable to malloc buf\n"); ret = -ENOMEM; goto err_no_mem; } hello->bufsize = bufsize; printk(KERN_DEBUG "Buffer size: %d \n", hello->bufsize); /* Alloc the device number dynamically which is stored in the first parameter */ ret = alloc_chrdev_region(&hello->devt, 0, 1, DEVICE_NAME); if(ret < 0){ printk("Alloc chrdev region error\n"); goto error_alloc_region; } printk(KERN_DEBUG "Major = %d, minor = %d\n", MAJOR(hello->devt), MINOR(hello->devt)); cdev_init(&hello->chrdev, &hello_fops); hello->chrdev.owner = THIS_MODULE; hello->chrdev.ops = &hello_fops; ret = cdev_add(&hello->chrdev, hello->devt, 1); if(ret < 0){ printk(KERN_ERR "Cdev add error\n"); goto error_cdev_add; } hello->cls = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(hello->cls)){ printk(KERN_ERR "Class create error\n"); ret = -1; goto error_class_create; } hello->dev = device_create(hello->cls, NULL, hello->devt, NULL, DEVICE_NAME); if(IS_ERR(hello->dev)){ printk(KERN_ERR "Create device error\n"); ret = -1; goto error_device_create; } return 0; error_device_create: error_class_create: cdev_del(&hello->chrdev); error_cdev_add: unregister_chrdev_region(hello->devt, 1); error_alloc_region: kfree(hello->buf); err_no_mem: kfree(hello); return ret; } static void __exit hello_exit(void) { dev_dbg(hello->dev, "Hello world exits\n"); cdev_del(&hello->chrdev); unregister_chrdev_region(hello->devt, 1); device_destroy(hello->cls, hello->devt); class_destroy(hello->cls); kfree(hello->buf); kfree(hello); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("yj4231@hotmail.com"); MODULE_DESCRIPTION("Hello template driver");
对应的测试程序如下:
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv) { int fd, ret; char data[512+1] = "Hello test"; char buf[512+1]; int n; fd = open("/dev/hello", O_RDWR); if(fd < 0){ perror("Open device fail"); return -1; } n = strlen(data); ret = write(fd, data, n); if(ret != n){ printf("Write failed\n"); exit(-1); } printf("Write retval: %d\n", ret); ret = read(fd, buf, n); if(ret != n){ printf("Read failed\n"); exit(-1); } buf[n] = '\0'; printf("Read retval %d, %s\n", ret, buf); sleep(1); close(fd); }
History:
2014.02.15 单进程,多线程共享buffer版本(2.6.32)。