#include <linux/module.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/sched.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #include "hello.h" #define DEV_NAME "hello" #define pr_inf(fmt, arg ...) \ printk(KERN_ERR "\033[32m[Mark] %04d : %s " fmt "\033[0m\n", __LINE__, __FUNCTION__, ##arg) /** * global driver data ptr */ struct __exitdata hello_android_dev *ddata = NULL; /********************************************************** * * /dev/hello have this list file operations * *********************************************************/ static int hello_open(struct inode *inode, struct file *filp) { pr_inf("open a file..."); return 0; } static int hello_release(struct inode *inode, struct file *filp) { pr_inf("close a file...\n"); return 0; } static ssize_t hello_read(struct file *flip, char __user *buf, size_t count, loff_t *offset) { pr_inf("reading..."); if (down_interruptible(&(ddata->sem))) { return -ERESTARTSYS; } if (count < 20) copy_to_user(buf, ddata->val, count); pr_inf("--> %s", ddata->val); up(&(ddata->sem)); return 0; } static ssize_t hello_write(struct file *flip, const char __user *buf, size_t count, loff_t *offset) { pr_inf("writing..."); if (down_interruptible(&(ddata->sem))) { return -ERESTARTSYS; } if (count < 20) copy_from_user(ddata->val, buf, count); pr_inf("<-- %s", buf); up(&(ddata->sem)); return 0; } static struct file_operations hello_fops = { .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write, }; /********************************************************** * * init this module driver data * *********************************************************/ int hello_ddata_init(void) { char buf[] = "Raw data"; ddata = kzalloc(sizeof(struct hello_android_dev), GFP_KERNEL); if (!ddata) { pr_inf("fail to alloc mem..."); return -ENOMEM; } ddata->dev_no = 0; sema_init(&(ddata->sem), 1); memcpy(ddata->val, buf, sizeof(buf)); return 0; } /********************************************************** * * register char device * create file /dev/hello /sys/class/hello/ * create attr file in /sys/class/hello * create proc file /proc/hello * *********************************************************/ int hello_reg_dev(void) { int ret; /** * you can read device use -- head /dev/hello & * through syscall to wirte ddata->val * c-code maybe good, but no echo "XX" > /dev/hello */ cdev_init(&(ddata->dev), &hello_fops); ddata->dev.owner = THIS_MODULE; /** * call __register_chrdev_region(major, baseminor, minorct, DEV_NAME), * which while major == 0 do auto alloc a dev_no */ ret = alloc_chrdev_region(&(ddata->dev_no), 0, 1, DEV_NAME); if (ddata->dev_no < 0) { pr_inf("fail to alloc region..."); return -ENODEV; } pr_inf("dev no : %d %d", MAJOR(ddata->dev_no), MINOR(ddata->dev_no)); /** * add a char device to kenel */ ret = cdev_add(&(ddata->dev), ddata->dev_no, 1); if (ret < 0) goto err_add_cdev; /** * create dirent /sys/class/hello */ ddata->class = class_create(THIS_MODULE, "mark"); if (IS_ERR(ddata->class)) { ret = PTR_ERR(ddata->class); pr_inf("fail to create /sys/class/mark/"); goto err_creat_class; } /** * create file /dev/hello & dirent /sys/class/mark/hello */ ddata->device = device_create(ddata->class, NULL, ddata->dev_no, "%s", DEV_NAME); if (IS_ERR(ddata->device)) { ret = PTR_ERR(ddata->device); pr_inf("fail to create /sys/class/hello/hello & /dev/hello"); goto err_create_device; } return 0; err_create_device: class_destroy(ddata->class); err_creat_class: cdev_del(&(ddata->dev)); err_add_cdev: unregister_chrdev_region(ddata->dev_no, 1); return ret; } /********************************************************** * * create the attr file to use attr method * *********************************************************/ static ssize_t hello_val_show(struct device *dev, struct device_attribute *attr, char *buf) { char val[20] = {0}; if (down_interruptible(&(ddata->sem))) { return -ERESTARTSYS; } memcpy(val, ddata->val, 20); up(&(ddata->sem)); pr_inf("--> %s", ddata->val); return snprintf(val, PAGE_SIZE, "%s\n", val); } static ssize_t hello_val_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { if (down_interruptible(&(ddata->sem))) { return -ERESTARTSYS; } if (count < 20) { memset(ddata->val, 0, 20); memcpy(ddata->val, buf, count); } up(&(ddata->sem)); pr_inf("<-- %s", count < 20 ? buf : "most store 20 char"); return count; } static DEVICE_ATTR(hello, S_IRUGO | S_IWUGO, hello_val_show, hello_val_store); /** * create a attr file /sys/class/mark/hello/hello * use cat & echo to write\read */ int hello_creat_attr(void) { /** * the MACRO -- DEVICE_ATTR define as : * struct device_attribute dev_attr_##_name = * __ATTR(_name, _mode, _show, _store) */ return device_create_file(ddata->device, &dev_attr_hello); } /********************************************************** * * module operations * *********************************************************/ static ssize_t hello_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) { char val[20] = {0}; if (off > 0) { *eof = 1; return 0; } if (down_interruptible(&(ddata->sem))) { return -ERESTARTSYS; } memcpy(val, ddata->val, sizeof(val)); up(&(ddata->sem)); pr_inf("--> %s", ddata->val); return snprintf(page, PAGE_SIZE, "%s\n", val); } static ssize_t hello_proc_write(struct file *flip, const char __user *buf, unsigned long len, void *data) { int err = 0; char *page = NULL; if (len > PAGE_SIZE) return -EFAULT; page = (char *)__get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; if (copy_from_user(page, buf, len)) { err = -EFAULT; goto out; } if (down_interruptible(&(ddata->sem))) { err = -ERESTARTSYS; goto out; } memset(ddata->val, 0, 20); memcpy(ddata->val, page, len); up(&(ddata->sem)); pr_inf("--> %s", ddata->val); return len; out: free_page((unsigned long)page); return err; } int hello_build_proc(void) { struct proc_dir_entry *entry; entry = create_proc_entry(DEV_NAME, 0777, NULL); if (entry) { entry->read_proc = hello_proc_read; entry->write_proc = hello_proc_write; return 0; } return -ENODEV; } /********************************************************** * * module operations * *********************************************************/ static __init int hello_init(void) { int ret; pr_inf("Driver init...\n\n"); pr_inf("[ %s ] get pid is : %i", current->comm, current->pid); ret = hello_ddata_init(); if (ret < 0) return ret; ret = hello_reg_dev(); if (ret < 0) goto err_reg_dev; ret = hello_creat_attr(); if (ret < 0) goto err_creat_attr; ret = hello_build_proc(); if (ret < 0) goto err_build_proc; return 0; err_build_proc: err_creat_attr: if (ddata && ddata->class) { device_destroy(ddata->class, ddata->dev_no); class_destroy(ddata->class); } if (ddata->dev_no) unregister_chrdev_region(ddata->dev_no, 1); if (ddata) cdev_del(&(ddata->dev)); err_reg_dev: kfree(ddata); ddata = NULL; return ret; } static __exit void hello_exit(void) { pr_inf("Driver destory...\n\n"); remove_proc_entry(DEV_NAME, NULL); if (ddata && ddata->class) { device_destroy(ddata->class, ddata->dev_no); class_destroy(ddata->class); } if (ddata->dev_no) unregister_chrdev_region(ddata->dev_no, 1); if (ddata) { cdev_del(&(ddata->dev)); kfree(ddata); ddata = NULL; } return; } MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark"); module_init(hello_init); module_exit(hello_exit);
#ifndef _HELLO_ANDROID_H_ #define _HELLO_ANDROID_H_ #include <linux/cdev.h> #include <linux/semaphore.h> #define HELLO_DEVICE_NODE_NAME "hello" #define HELLO_DEVICE_FILE_NAME "hello" #define HELLO_DEVICE_PROC_NAME "hello" #define HELLO_DEVICE_CLASS_NAME "hello" struct hello_android_dev { char val[20]; struct semaphore sem; struct cdev dev; dev_t dev_no; struct class *class; struct device *device; }; #endif
浙公网安备 33010602011771号