2012年(1008)
分类:
2012-08-01 11:04:37
原文地址:Linux驱动学习实例 作者:luozhiyong131
/*
* 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