本demo基于Android2.3模拟器开发,内核代码(Linux2.6.29)和Android代码可以在网上下载、这里不在说明。
Android2.3及Linux2.6.29内核模拟器版本编译与调试
ldd3部分讲述总线驱动match以及设备驱动probe等,见Linux总线、设备与驱动——ldd3中demo分析
一、驱动
1.总线驱动
功能:总线驱动;提供设备注册和设备驱动注册以及设备与设备驱动匹配等函数功能。
testbus.c
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include "lddbus.h" MODULE_AUTHOR("Jonathan Corbet"); MODULE_LICENSE("Dual BSD/GPL"); static char *Version = "$Revision: 1.9 $"; /* * Respond to hotplug events. */ static int ldd_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size){ printk("TK---testlddbus.c----->>>>ldd_uevent\n"); envp[0] = buffer; if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", Version) >= buffer_size) return -ENOMEM; envp[1] = NULL; return 0; } static int ldd_match(struct device *dev, struct device_driver *driver){ printk("TK---testbus.c----->>>>ldd_match\n"); return !strncmp(dev->init_name, driver->name, strlen(driver->name)); } static void ldd_bus_release(struct device *dev){ printk("TK---testbus.c-->>>>ldd_bus_release\n"); } struct bus_type ldd_bus_type = { .name = "ldd", .match = ldd_match, .uevent = ldd_uevent, }; struct device ldd_bus = { .init_name = "ldd0", .release = ldd_bus_release }; 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 ldd_dev_release(struct device *dev){ printk("TK-------->>>testubs.c>>>>>>ldd_dev_release\n"); } int register_ldd_device(struct ldd_device *ldddev){ ldddev->dev.bus = &ldd_bus_type; ldddev->dev.parent = &ldd_bus; ldddev->dev.release = ldd_dev_release; ldddev->dev.init_name = ldddev->name; return device_register(&ldddev->dev); } EXPORT_SYMBOL(register_ldd_device); void unregister_ldd_device(struct ldd_device *ldddev){ device_unregister(&ldddev->dev); } EXPORT_SYMBOL(unregister_ldd_device); static int lddbus_drv_probe(struct device *_dev){ printk("TK---testlddbus.c----->>>>ldd_drv_probe\n"); struct ldd_driver *drv = to_ldd_driver(_dev->driver); struct ldd_device *dev = to_ldd_device(_dev); return drv->probe(dev); } static int lddbus_drv_remove(struct device *_dev){ printk("TK---testlddbus.c----->>>>ldd_drv_remove\n"); struct ldd_driver *drv = to_ldd_driver(_dev->driver); struct ldd_device *dev = to_ldd_device(_dev); return drv->remove(dev); } static ssize_t show_version(struct device_driver *driver, char *buf){ struct ldd_driver *ldriver = to_ldd_driver(driver); sprintf(buf, "%s\n", ldriver->version); return strlen(buf); } int register_ldd_driver(struct ldd_driver *driver){ int ret; driver->driver.bus = &ldd_bus_type; if (driver->probe) driver->driver.probe = lddbus_drv_probe; if (driver->remove) driver->driver.remove = lddbus_drv_remove; ret = driver_register(&driver->driver); if (ret) return ret; driver->version_attr.attr.name = "version"; driver->version_attr.attr.mode = S_IRUGO; driver->version_attr.show = show_version; driver->version_attr.store = NULL; return driver_create_file(&driver->driver, &driver->version_attr); } void unregister_ldd_driver(struct ldd_driver *driver){ driver_unregister(&driver->driver); } EXPORT_SYMBOL(register_ldd_driver); EXPORT_SYMBOL(unregister_ldd_driver); static int __init ldd_bus_init(void){ printk("TK---testlddbus.c----->>>>ldd_bus_init\n"); int ret; ret = bus_register(&ldd_bus_type); if (ret) return ret; if (bus_create_file(&ldd_bus_type, &bus_attr_version)) printk(KERN_NOTICE "Unable to create version attribute\n"); ret = device_register(&ldd_bus); if (ret) printk(KERN_NOTICE "Unable to register ldd0\n"); return ret; } static void ldd_bus_exit(void){ printk("TK---testlddbus.c----->>>>ldd_bus_exit\n"); device_unregister(&ldd_bus); bus_unregister(&ldd_bus_type); } module_init(ldd_bus_init); module_exit(ldd_bus_exit);2.设备模块
功能:模拟设备热插拔事件;insmod为插入设备,rmmod为删除设备。
testdev.c
#include <linux/platform_device.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> //#include <asm/arch/map.h> #include <asm/io.h> #include <linux/irq.h> #include <linux/wait.h> #include <linux/semaphore.h> #include <linux/module.h> #include <linux/fs.h> #include "lddbus.h" #include <linux/sched.h> static struct ldd_device mini_device = { .name = "mini", }; static int __init dev_init(void) { printk("TK---testdev.c----->>>>mini_init\n"); return register_ldd_device(&mini_device); } static void __exit dev_exit(void) { printk("TK---testdev.c----->>>>mini_exit\n"); return unregister_ldd_device(&mini_device); } module_init(dev_init); module_exit(dev_exit); MODULE_AUTHOR("ljf"); MODULE_LICENSE("Dual BSD/GPL");3.设备驱动模块
功能:上述2的设备驱动程序;支持热插拔,目前提供设备节点的创建及open、close、read和write接口。
testdriver.c
#include <linux/platform_device.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <asm/io.h> #include <linux/irq.h> #include <linux/wait.h> #include <linux/semaphore.h> #include <linux/module.h> #include <linux/fs.h> #include "lddbus.h" #include <linux/sched.h> #include <asm/uaccess.h> #define MYMODULE_MAJOR 248 struct class *mymodule_class; struct device *my_device; EXPORT_SYMBOL(mymodule_class); static ssize_t s3c2440mini_read(struct file * file, char __user * userbuf, size_t count, loff_t * off){ printk ("TK-------->>>testdriver.c>>>>>>s3c2440mini_read\n"); char cmd[20]; strcpy(cmd,"456"); copy_to_user(userbuf,cmd, 3); return 0; } static ssize_t s3c2440mini_write(struct file *file, const char __user *data, size_t len, loff_t *ppos){ printk ("TK-------->>>testdriver.c>>>>>>s3c2440mini_write\n"); char cmd[20]; copy_from_user(cmd, data, 3); printk ("TK-------->>>testdriver.c>>>>>>s3c2440mini_write>>>>cmd is %s\n",cmd); return 0; } static int s3c2440mini_open(struct inode *inode, struct file *file){ printk("TK-------->>>testdriver.c>>>>>>s3c2440mini_open\n");//add by tankai return 0; } static int s3c2440mini_release(struct inode *inode, struct file *file){ printk ("TK-------->>>testdriver.c>>>>>s3c2440mini_release\n"); return 0; } static struct file_operations mini_ops = { .owner = THIS_MODULE, .write = s3c2440mini_write, .read = s3c2440mini_read, .release = s3c2440mini_release, .open = s3c2440mini_open, }; static int mini_probe (struct ldd_device * dev){ printk("TK----testdriver.c------>>>>driver_probe %s\n",dev->name); int err = 0; //只有register_chrdev不会自动创建设备节点;需要手动mknod if (register_chrdev(MYMODULE_MAJOR, "mymodule", &mini_ops)) { printk(KERN_ERR "mymodule: unable to get major %d\n", MYMODULE_MAJOR); err = -EIO; goto out; } mymodule_class = class_create(THIS_MODULE, "mymodule"); if (IS_ERR(mymodule_class)) { err = PTR_ERR(mymodule_class); goto out_chrdev; } //device_create是自动创建设备节点的关键 my_device = device_create(mymodule_class, NULL, MKDEV(MYMODULE_MAJOR, 0), NULL, "mymod0"); //该设备的父设备即刚才总线发现的ldd设备 //my_device->parent = &(dev->dev); err = 0; goto out; out_chrdev: unregister_chrdev(MYMODULE_MAJOR, "mymodule"); out: return err; } static int mini_remove(struct ldd_device * dev){ printk("TK----testdriver.c------>>>>driver_remove %s\n",dev->name); device_destroy(mymodule_class, MKDEV(MYMODULE_MAJOR, 0)); class_destroy(mymodule_class); unregister_chrdev(MYMODULE_MAJOR, "mymodule"); } static struct ldd_driver mini_driver = { .version = "$Revision: 1.21 $", .module = THIS_MODULE, .probe = mini_probe, .remove = mini_remove, .driver = { .name = "mini", }, }; static int __init mini_init(void) { printk("TK---testdriver.c----->>>>driver_init\n"); return register_ldd_driver(&mini_driver); } static void __exit mini_exit(void) { printk("TK---testdriver.c----->>>>driver_exit\n"); return unregister_ldd_driver(&mini_driver); } module_init(mini_init); module_exit(mini_exit); MODULE_AUTHOR("ljf"); MODULE_LICENSE("Dual BSD/GPL");4.lddbus.h
/* * Definitions for the virtual LDD bus. * * $Id: lddbus.h,v 1.4 2004/08/20 18:49:44 corbet Exp $ */ //extern struct device ldd_bus; extern struct bus_type ldd_bus_type; /* * The LDD driver type. */ /* * A device type for things "plugged" into the LDD bus. */ struct ldd_device { char *name; //struct ldd_driver *driver; struct device dev; }; #define to_ldd_device(x) container_of((x), struct ldd_device, dev) struct ldd_driver { char *version; struct module *module; int (*probe)(struct ldd_device *); int (*remove)(struct ldd_device *); void (*shutdown)(struct ldd_device *); int (*suspend)(struct ldd_device *, pm_message_t state); int (*resume)(struct ldd_device *); struct device_driver driver; struct driver_attribute version_attr; }; #define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver) extern int lddbus_kill(struct ldd_device *dev); extern int register_ldd_device(struct ldd_device *); extern void unregister_ldd_device(struct ldd_device *); extern int register_ldd_driver(struct ldd_driver *); extern void unregister_ldd_driver(struct ldd_driver *);5.Makefile
obj-m := testbus.o testdriver.o testdev.o PWD := $(shell pwd) KERNELDIR := /home/android2.3/android2.3_kernel/ default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules # cp -rf mini.ko ../module/ # cp -rf lddbus.ko ../module/ clean: rm *.mod.c *.o *.ko *.bak modules.* Module.*编译后分别为testbus.ko,testdev.ko和testdriver.ko。
二、测试用例
1.testldd.c
#include <fcntl.h> #include <stdlib.h> #include <sys/mman.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> int main(){ int fd = open("/dev/mymod0",O_RDWR,0); printf("TK------->>>fd is %d\n",fd); char buf[20]; int result = read(fd,&buf,3); printf("TK------->>>readresult is %d,buf is %s\n",result,buf); strcpy(buf,"123"); result = write(fd,&buf,3); printf("TK------->>>writeresult is %d,buf is %s\n",result,buf); close(fd); return 0; }2.Android.mk
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ testldd.c LOCAL_SHARED_LIBRARIES := \ libutils LOCAL_MODULE:= testldd LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE)三、运行
1.insmod testbus.ko
结果:
TK---testlddbus.c----->>>>ldd_bus_initll /sys/devices/ldd0/
drwxr-xr-x root root 2013-09-16 09:29 power -rw-r--r-- root root 4096 2013-09-16 09:29 ueventll /sys/bus/ldd/
drwxr-xr-x root root 2013-09-16 09:46 devices drwxr-xr-x root root 2013-09-16 09:46 drivers -rw-r--r-- root root 4096 2013-09-16 09:30 drivers_autoprobe --w------- root root 4096 2013-09-16 09:30 drivers_probe --w------- root root 4096 2013-09-16 09:30 uevent -r--r--r-- root root 4096 2013-09-16 09:30 version2.insmod testdriver.ko
结果:
TK---testdriver.c----->>>>driver_initll /sys/bus/ldd/drivers/
drwxr-xr-x root root 2013-09-16 09:47 mini3.模拟设备插入insmod testdev.ko( 实际这部分有总线驱动完成!!)
结果:
TK---testdev.c----->>>>mini_init TK---testlddbus.c----->>>>ldd_uevent TK---testbus.c----->>>>ldd_match TK---testlddbus.c----->>>>ldd_drv_probe TK----testdriver.c------>>>>driver_probe minill /sys/devices/ldd0/
drwxr-xr-x root root 2013-09-16 09:48 mini drwxr-xr-x root root 2013-09-16 09:29 power -rw-r--r-- root root 4096 2013-09-16 09:29 ueventll /sys/bus/ldd/devices/
lrwxrwxrwx root root 2013-09-16 09:36 mini -> ../../../devices/ldd0/minill /dev/mymod0
crw------- root root 248, 0 2013-09-16 09:48 mymod04.测试
./testldd
TK-------->>>testdriver.c>>>>>>s3c2440mini_open TK------->>>fd is 3 TK-------->>>testdriver.c>>>>>>s3c2440mini_read TK------->>>readresult is 0,buf is 456��`ү� TK-------->>>testdriver.c>>>>>>s3c2440mini_write TK-------->>>testdriver.c>>>>>>s3c2440mini_write>>>>cmd is 123�TG�0��`��`ͥ��K�� TK-------->>>testdriver.c>>>>>s3c2440mini_release TK------->>>writeresult is 0,buf is 123