Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7724011
  • 博文数量: 961
  • 博客积分: 15795
  • 博客等级: 上将
  • 技术积分: 16612
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-07 14:23
文章分类

全部博文(961)

文章存档

2016年(1)

2015年(61)

2014年(41)

2013年(51)

2012年(235)

2011年(391)

2010年(181)

分类: 嵌入式

2011-09-29 14:19:49

/*

 * Linux驱动学习实例

 *  字符设备、并发控制、设备驱动模型简单综合应用

 * Lzy     2011-9-27 2011-9-29

 */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

 

#define MEMDEV_MAJOR 251   /*预设的mem的主设备号*/

#define MEMDEV_NR_DEVS 2    /*设备数*/

#define MEMDEV_SIZE 4096

 

struct mem_dev  

{   

    char *data;

    unsigned long size;

    struct semaphore sem;     /* 定义信号量 */

    wait_queue_head_t inq;

};

 

 

struct cdev mem_cdev;

struct mem_dev *mem_devp;

static  int mem_major = MEMDEV_MAJOR;

 

static char msg[255];

struct proc_dir_entry *mem_dir, *mem_file;

 

/* 定义幻数 */

#define MEMDEV_IOC_MAGIC  'k'

 

/* 定义命令 */

#define MEMDEV_IOCPRINT   _IO(MEMDEV_IOC_MAGIC, 1)

#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)

#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)

#define MEMDEV_IOC_MAXNR 3

 

int have_data = 0; /*表明设备有足够数据可供读*/

 

static int mem_open(struct inode *inode, struct file *filp)

{

    struct mem_dev *dev;

 

    int num = MINOR(inode->i_rdev);

   

    if(num >= MEMDEV_NR_DEVS)       

        return -ENODEV;

 

    dev = &mem_devp[num];

    filp->private_data = dev;

 

    return 0;

}

 

static int mem_release(struct inode *inode, struct file *filp)

{

    return 0;

}

 

static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)

{

    unsigned long p =  *ppos; 

    unsigned int count = size; 

    int ret = 0; 

 

    struct mem_dev *dev = filp->private_data;

   

    if (p >= MEMDEV_SIZE)  

        return 0;

    if (count > MEMDEV_SIZE - p)   

        count = MEMDEV_SIZE - p;

 

    wait_event_interruptible(dev->inq, have_data);

   

    if(copy_to_user(buf, (void *)dev->data + p, count))

        ret =  - EFAULT;

    else

    {

        *ppos += count;   

        ret = count;      

      //  printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);

    }

   

    have_data = 0;

   

    return ret;

}

 

static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)

{   

    unsigned long p =  *ppos; 

    unsigned int count = size; 

    int ret = 0; 

 

    struct mem_dev *dev = filp->private_data;

 

    if(down_interruptible(&dev->sem))

        return -ERESTARTSYS;

   

    if(p >= MEMDEV_SIZE)  

        return 0;

    if(count > MEMDEV_SIZE - p)   

        count = MEMDEV_SIZE - p;

 

    if(copy_from_user(dev->data + p, buf, count))

    {

        ret =  - EFAULT;

        goto out;

    }

    else

    {

        *ppos += count;   

        ret = count;       

       // printk(KERN_INFO "write %d bytes(s) from %d\n", count, p);

    }

 

    have_data = 1;

    wake_up(&(dev->inq));

 

out:

    up(&dev->sem);

 

    return ret;

}

 

static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)

{

    loff_t newpos;

 

    switch(whence)

    {

    case 0:

        newpos = offset;

        break;

    case 1:

        newpos = filp->f_pos + offset;

        break;

    case 2:

        newpos = MEMDEV_SIZE -1 + offset;

        break;

    default :

        return -EINVAL;

    }

 

    if ((newpos<0) || (newpos>MEMDEV_SIZE)) 

        return -EINVAL;

 

    filp->f_pos = newpos;

   

    return newpos;

}

 

static int mem_ioctl(struct inode *inode, struct file *filp,

                 unsigned int cmd, unsigned long arg)

{           

        int ret = 0;   

        int ioarg = 0;

 

    if(_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC)

        return -EINVAL;

    if(_IOC_NR(cmd) > MEMDEV_IOC_MAXNR)

        return -EINVAL;

 

    switch(cmd)

    {

    case MEMDEV_IOCPRINT:

        printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");

        break;

       

    case MEMDEV_IOCGETDATA:

        ioarg = 1101;

        ret = put_user(ioarg, (int *)arg);

        break;

   

    case MEMDEV_IOCSETDATA:

        ret = get_user(ioarg, (int *)arg);

        printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);

        break;

 

      default: 

        return -EINVAL;   

    }

    return ret;

}

 

static unsigned int mem_poll (struct file *filp, poll_table *wait)

{

    struct mem_dev  *dev = filp->private_data;

    unsigned int mask = 0;

 

    poll_wait(filp, &dev->inq, wait);

 

    if(have_data)

        mask = POLLIN | POLLRDNORM;

       

    return mask;

}

 

static int mem_mmap (struct file *filp, struct vm_area_struct *vma)

{

    struct mem_dev *dev = filp->private_data;

 

    vma->vm_flags |= VM_IO;

    vma->vm_flags |= VM_RESERVED;

   

    if(remap_pfn_range(vma, vma->vm_start, virt_to_phys(dev->data)>>PAGE_SHIFT,

                    vma->vm_end - vma->vm_start,

                    vma->vm_page_prot))

        return  -EAGAIN;

    return 0;

}

 

static const struct file_operations mem_fops = {

    .owner = THIS_MODULE,

    .open = mem_open,

    .release = mem_release,

    .read = mem_read,

    .write = mem_write,

    .llseek = mem_llseek,

    .ioctl = mem_ioctl,

    .poll = mem_poll,

    .mmap = mem_mmap

};

 

static int mem_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)

{

    int len = strlen(mem_devp[0].data);

    if(off >= len)

        return 0;

    if(count > len - off)

        count = len - off;

   

    memcpy(page + off, mem_devp[0].data + off, count);

    return off + count;

}

 

static int mem_write_proc(struct file *file, const char __user *buffer,

                        unsigned long count, void *data)

{

    int len = MEMDEV_SIZE;

   

    if(count > len - 1)

        count = len - 1;

   

    if(copy_from_user(mem_devp[0].data, buffer, count))

         return -EFAULT;

        

    msg[count] = '\0';

 

    return count;

}

 

static void obj_test_release (struct kobject *kobj)

{

    printk("kobject_test: release .\n");

}

 

  

static ssize_t kobj_test_show(struct kobject *kobject,

                              struct attribute *attr,char *buf)

{

    sprintf(buf,"%s\n",attr->name);

   

    return strlen(attr->name)+2;

}

 

static ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)

{

    printk("write: %s\n",buf);

    strcpy(attr->name,buf);

   

    return count;

}

 

struct sysfs_ops obj_test_sysops = {

    .show = kobj_test_show,

    .store = kobj_test_store,

};

 

static struct attribute test_attr = {

    .name = "kobj_config",

    .mode = S_IRWXUGO,

};

 

static struct attribute *def_attrs[] = {

    &test_attr,

    NULL,

};

 

struct kobj_type ktype = {

    .release = obj_test_release,

    .sysfs_ops = &obj_test_sysops,

    .default_attrs = def_attrs,

};

 

struct kobject kobj;

 

struct kset kset_p;

struct kset kset_c;

 

static int kset_filter(struct kset *kset, struct kobject *kobj)

{

    printk("Filter: kobj %s.\n",kobj->name);

    return 1;

}

 

static const char *kset_name(struct kset *kset, struct kobject *kobj)

{

    static char buf[20];

    printk("Name: kobj %s.\n",kobj->name);

    sprintf(buf,"%s","kset_name");

 

    return buf;

}

static int kset_uevent(struct kset *kset, struct kobject *kobj,

              struct kobj_uevent_env *env)

{

    int i = 0;

    printk("uevent: kobj %s.\n",kobj->name);

 

     while( i < env->envp_idx){

        printk("%s.\n",env->envp[i]);

        i++;

     }

   

    return 0;

}

 

 

struct kset_uevent_ops uevent_ops = {

    .filter = kset_filter,

    .name = kset_name,

    .uevent = kset_uevent,

};

 

static int my_match(struct device *dev, struct device_driver *driver)

{

    return !strncmp(dev->bus_id, driver->name, strlen(driver->name));

}  

 

 

struct bus_type my_bus_type = {

    .name = "my_bus",

    .match = my_match,   

};

 

static char *Version = "$Revision: 1.0 $";

 

static ssize_t show_bus_version(struct bus_type *bus, char *buf)

{  

    return snprintf(buf, PAGE_SIZE, "%s\n", Version);

}

 

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

 

static void my_bus_release(struct device *dev)

{

    printk(KERN_DEBUG "my bus release\n");

}

 

struct device my_bus = {

    .name = "my_bus",

    .release = my_bus_release,

};

 

static void my_dev_release(struct device *dev)

{

   

}

 

struct device my_dev = {

    .bus = &my_bus_type,

    .parent = &my_bus,

    .release = my_dev_release,

};

 

static int my_probe(struct device *dev)

{

    printk("Driver found device which my driver can handle!\n");

    return 0;

}

 

static int my_remove(struct device *dev)

{

    printk("Driver found device unpluged!\n");

    return 0;

}

 

struct device_driver my_driver = {

    .name = "my_dev",

    .bus = &my_bus_type,

    .probe = my_probe,

    .remove    = my_remove,

};

static ssize_t mydriver_show(struct device_driver *driver, char *buf)

{

    return sprintf(buf, "%s\n", "This is my driver!");

}

 

static DRIVER_ATTR(drv, S_IRUGO, mydriver_show, NULL);

 

static struct platform_device *my_platform_device;

 

static int my_platform_probe(struct device *dev)

{

    printk("Driver found device which my driver can handle!\n");

    return 0;

}

 

static int my_platform_remove(struct device *dev)

{

    printk("Driver found device unpluged!\n");

    return 0;

}

 

static struct platform_driver my_platform_driver = {

    .probe       = my_platform_probe,

    .remove        = my_platform_remove,

    .driver        = {

        .owner    = THIS_MODULE,

        .name    = "my_platform_dev",

    },

};

 

static int __init memdev_init(void)

{

    int result, i;

    dev_t devno = MKDEV(mem_major, 0);

 

    if(mem_major)

        result = register_chrdev_region(devno, MEMDEV_NR_DEVS, "memdev");

    else

    {

        alloc_chrdev_region(&devno, 0, MEMDEV_NR_DEVS, "memdev");

        result = mem_major = MAJOR(devno);

    }

 

    if(result < 0)

        return result;

   

    cdev_init(&mem_cdev, &mem_fops);

    mem_cdev.owner = THIS_MODULE;

    mem_cdev.ops = &mem_fops;

 

    cdev_add(&mem_cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);

 

    mem_devp = kmalloc(sizeof(struct mem_dev) * MEMDEV_NR_DEVS, GFP_KERNEL);

    if(!mem_devp)

    {

        result =  - ENOMEM;  

        goto fail_malloc;

    }

    memset(mem_devp, 0, sizeof(struct mem_dev));

 

    for(i = 0; i < MEMDEV_NR_DEVS; i++)

    {

        mem_devp[i].size = MEMDEV_SIZE;

        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);

        memset(mem_devp[i].data, 0, MEMDEV_SIZE);

        sema_init(&mem_devp[i].sem, 1);

        init_waitqueue_head(&mem_devp[i].inq);   /*初始化等待队列*/

    }

 

    /*proc 操作*/

    mem_dir = proc_mkdir("mem_dir", NULL);

    if(!mem_dir)

    {

         printk(KERN_ERR "Can't create /proc/mem_dir\n");

         return -1;

    }

    mem_file = create_proc_entry("mem", 0666, mem_dir);

    if(!mem_file)

    {

         printk(KERN_ERR "Can't create /proc/mem_dir/mem_file\n");

         return -1;

    }

 

    mem_file->read_proc = mem_read_proc;

    mem_file->write_proc = mem_write_proc;

   

   

    kobject_init_and_add(&kobj, &ktype, NULL, "kobject_test");

 

    kobject_set_name(&kset_p.kobj,"kset_p");

    kset_p.uevent_ops = &uevent_ops;

    kset_register(&kset_p);

 

    result = bus_register(&my_bus_type);

    if(result)

        return result;

 

    if(bus_create_file(&my_bus_type, &bus_attr_version))

        printk(KERN_NOTICE "Fail to create version attribute!\n");

 

    result = device_register(&my_bus);

    if (result)

        printk(KERN_NOTICE "Fail to register device:my_bus!\n");

 

 

    strncpy(my_dev.bus_id, "my_dev", BUS_ID_SIZE);

    device_register(&my_dev);

    device_create_file(&my_dev, &dev_attr_dev);

 

    driver_register(&my_driver);

    driver_create_file(&my_driver, &driver_attr_drv);

 

    my_platform_device = platform_device_alloc("my_platform_device", -1);

    result = platform_device_add(my_platform_device);

    if(result)

        platform_device_put(my_platform_device);

 

    platform_driver_register(&my_platform_driver);

   

fail_malloc:

    unregister_chrdev_region(MKDEV(mem_major, 0), MEMDEV_NR_DEVS);

   

    return result;

 

}

 

static void __exit memdev_exit(void)

{

    cdev_del(&mem_cdev);

    kfree(mem_devp);

    unregister_chrdev_region(MKDEV(mem_major, 0), MEMDEV_NR_DEVS);

 

    kobject_del(&kobj);

    kset_unregister(&kset_p);

 

    driver_unregister(&my_driver);

    device_unregister(&my_dev);

    device_unregister(&my_bus);

    bus_unregister(&my_bus_type);

 

    platform_driver_unregister(&my_platform_driver);

    platform_device_unregister(my_platform_device);

}

 

module_init(memdev_init);

module_exit(memdev_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Lzy");

 

 源代码: Linux驱动学习实例.rar   

阅读(2508) | 评论(0) | 转发(5) |
给主人留下些什么吧!~~