本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_init
ll /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 uevent
ll /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 version
2.insmod testdriver.ko
结果:
TK---testdriver.c----->>>>driver_init
ll /sys/bus/ldd/drivers/
drwxr-xr-x root root 2013-09-16 09:47 mini
3.模拟设备插入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 mini
ll /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 uevent
ll /sys/bus/ldd/devices/
lrwxrwxrwx root root 2013-09-16 09:36 mini -> ../../../devices/ldd0/mini
ll /dev/mymod0
crw------- root root 248, 0 2013-09-16 09:48 mymod0
4.测试
./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