《Linux匯流排、裝置與驅動》自己寫的demo
阿新 • • 發佈:2019-02-03
本demo基於Android2.3模擬器開發,核心程式碼(Linux2.6.29)和Android程式碼可以在網上下載、這裡不在說明。
一、驅動
1.匯流排驅動
功能:匯流排驅動;提供設備註冊和裝置驅動註冊以及裝置與裝置驅動匹配等函式功能。
testbus.c
2.裝置模組#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);
功能:模擬裝置熱插拔事件;insmod為插入裝置,rmmod為刪除裝置。
testdev.c
3.裝置驅動模組#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");
功能:上述2的裝置驅動程式;支援熱插拔,目前提供裝置節點的建立及open、close、read和write介面。
testdriver.c
4.lddbus.h#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");
/*
* 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/mymod0crw------- 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