[置顶] 《Linux总线、设备与驱动》自己写的demo

本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

你可能感兴趣的:([置顶] 《Linux总线、设备与驱动》自己写的demo)