commit d98782fd8b7c390ab948c4b53b249c3c454d0ede Author: LIU CHAO Date: Sat Jul 22 13:42:32 2023 +0800 first commit diff --git a/device_model/Makefile b/device_model/Makefile new file mode 100644 index 0000000..d81f0c2 --- /dev/null +++ b/device_model/Makefile @@ -0,0 +1,30 @@ + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DDEBUG +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +#xxx-objs := main.o pipe.o access.o +obj-m := my_bus.o my_device.o my_driver.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + + +#depend .depend dep: +# $(CC) $(EXTRA_CFLAGS) -M *.c > .depend + +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif \ No newline at end of file diff --git a/device_model/my_bus.c b/device_model/my_bus.c new file mode 100644 index 0000000..1060517 --- /dev/null +++ b/device_model/my_bus.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pub.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + + +static int my_bus_match(struct device *dev, struct device_driver *drv){ + PDEBUG("%s-%s\n",__FILE__, __func__); + if(!strncmp(dev_name(dev), drv->name, strlen(drv->name))) + { + PDEBUG("dev & drv match\n"); + } +} + +static struct bus_type my_bus = { + .name = "my_bus", + .match = my_bus_match, +}; + +EXPORT_SYMBOL(my_bus); + +static char *version = "$Revision: 1.9 $"; + +static ssize_t my_bus_test_show(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%s\n", version); +} + +static BUS_ATTR(Version, S_IRUSR, my_bus_test_show, NULL); + + +static __init int my_bus_init(void) +{ + printk("my_bus init\n"); + + bus_register(&my_bus); + bus_create_file(&my_bus, &bus_attr_Version); + return 0; +} +module_init(my_bus_init); + + +static __exit void my_bus_exit(void) +{ + printk("my_bus exit\n"); + bus_remove_file(&my_bus, &bus_attr_Version); + bus_unregister(&my_bus); +} +module_exit(my_bus_exit); + + + + diff --git a/device_model/my_device.c b/device_model/my_device.c new file mode 100644 index 0000000..c0234a0 --- /dev/null +++ b/device_model/my_device.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pub.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + + +extern struct bus_type my_bus; + +void my_dev_release(struct device *dev) +{ + printk("%s-%s\n", __FILE__, __func__); +} + + +static struct device my_dev = { + .init_name = "my_dev", + .bus = &my_bus, + .release = my_dev_release, +}; + + +unsigned long id = 0; +ssize_t my_dev_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", id); +} + +ssize_t my_dev_id_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + kstrtoul(buf, 10, &id); + return count; +} + + +DEVICE_ATTR(my_dev_id, S_IRUSR|S_IWUSR, my_dev_id_show, my_dev_id_store); + +static __init int my_dev_init(void) +{ + printk("my_dev init\n"); + device_register(&my_dev); + device_create_file(&my_dev, &dev_attr_my_dev_id); + return 0; +} +module_init(my_dev_init); + + +static __exit void my_dev_exit(void) +{ + printk("xdev exit\n"); + device_remove_file(&my_dev, &dev_attr_my_dev_id); + device_unregister(&my_dev); +} +module_exit(my_dev_exit); + + + diff --git a/device_model/my_driver.c b/device_model/my_driver.c new file mode 100644 index 0000000..ab57174 --- /dev/null +++ b/device_model/my_driver.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pub.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + + +extern struct bus_type my_bus; + +int my_drv_probe(struct device *dev) +{ + printk("%s\n", dev_name(dev)); + printk("%s-%s\n", __FILE__, __func__); + return 0; +} + +int my_drv_remove(struct device *dev) +{ + printk("%s\n", dev->init_name); + printk("%s-%s\n", __FILE__, __func__); + return 0; +} + +static struct device_driver my_drv = { + .name = "my_dev", + .bus = &my_bus, + .probe = my_drv_probe, + .remove = my_drv_remove, +}; + + +char *name = "my_drv"; +ssize_t drvname_show(struct device_driver *drv, char *buf) +{ + return sprintf(buf, "%s\n", name); +} + +DRIVER_ATTR_RO(drvname); + +static __init int my_drv_init(void) +{ + printk("my_drv init\n"); + driver_register(&my_drv); + driver_create_file(&my_drv, &driver_attr_drvname); + return 0; +} +module_init(my_drv_init); + +static __exit void my_drv_exit(void) +{ + printk("my_drv exit\n"); + driver_remove_file(&my_drv, &driver_attr_drvname); + driver_unregister(&my_drv); +} +module_exit(my_drv_exit); + + + diff --git a/device_model/pub.h b/device_model/pub.h new file mode 100644 index 0000000..417e668 --- /dev/null +++ b/device_model/pub.h @@ -0,0 +1,11 @@ +#ifndef _PUB_H +#define _PUB_H + + +#ifdef DEBUG +#define PDEBUG(fmt, args...) printk(KERN_INFO "device model:: " fmt, ##args) +#else +#define PDEBUG(fmt, args...) +#endif + +#endif diff --git a/device_tree/Makefile b/device_tree/Makefile new file mode 100644 index 0000000..1b57120 --- /dev/null +++ b/device_tree/Makefile @@ -0,0 +1,30 @@ + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DDEBUG +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +#xxx-objs := main.o pipe.o access.o +obj-m := dt_plat_drv.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + + +#depend .depend dep: +# $(CC) $(EXTRA_CFLAGS) -M *.c > .depend + +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif \ No newline at end of file diff --git a/device_tree/dt_plat_drv.c b/device_tree/dt_plat_drv.c new file mode 100644 index 0000000..6327e13 --- /dev/null +++ b/device_tree/dt_plat_drv.c @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +struct led_chrdev { + unsigned int __iomem *va_dr; + unsigned int __iomem *va_gdir; + unsigned int __iomem *va_iomuxc_mux; + unsigned int __iomem *va_ccm_ccgrx; + unsigned int __iomem *va_iomux_pad; + unsigned int led_pin; + unsigned int clock_offset; + struct cdev dev; + dev_t my_devno; +}; + +static struct class *led_class = NULL; + +static int led_open(struct inode *inode, struct file *filp) +{ + uint32_t val = 0; + struct led_chrdev *my_led_dev = NULL; + + printk("led open %d:%d...\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev)); + my_led_dev = container_of(inode->i_cdev, struct led_chrdev, dev); + + filp->private_data = my_led_dev; + + val = ioread32(my_led_dev->va_ccm_ccgrx); + val &= ~(3 << my_led_dev->clock_offset); + val |= 3 << my_led_dev->clock_offset; + + iowrite32(val, my_led_dev->va_ccm_ccgrx); + iowrite32(5, my_led_dev->va_iomuxc_mux); + iowrite32(0x1F838, my_led_dev->va_iomux_pad); + + val = ioread32(my_led_dev->va_gdir); + val &= ~(1 << my_led_dev->led_pin); + val |= (1 << my_led_dev->led_pin); + iowrite32(val, my_led_dev->va_gdir); + + val = ioread32(my_led_dev->va_dr); + val |= (0x01 << my_led_dev->led_pin); + iowrite32(val, my_led_dev->va_dr); + + return 0; +} + +static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) +{ + return cnt; +} + +static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt) +{ + uint32_t val = 0; + uint32_t ret = 0; + struct led_chrdev *my_led_dev = NULL; + + printk("led write...\n"); + my_led_dev = filp->private_data; + + kstrtoul_from_user(buf,cnt,10,&ret); + + //printk("ret = %u\n",ret); + + val = ioread32(my_led_dev->va_dr); + + if (ret) + val &= ~(0x01 << my_led_dev->led_pin); + else + val |= (0x01 << my_led_dev->led_pin); + + iowrite32(val, my_led_dev->va_dr); + *offt += cnt; + + return cnt; + +} + +static int led_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static struct file_operations led_fops = { +.owner = THIS_MODULE, +.open = led_open, +.read = led_read, +.write = led_write, +.release = led_release, +}; + + +static struct platform_device_id led_pdev_ids[] = { + {.name = "led_pdev"}, + {} +}; +MODULE_DEVICE_TABLE(platform, led_pdev_ids); + + +int led_plat_probe(struct platform_device *pdev) +{ + uint32_t val = 0; + int ret = 0; + dev_t devno = 0; + struct led_chrdev *my_led_dev = NULL; + struct device_node *dev_node = NULL; + char *compatible_str = NULL; + + unsigned int *led_hwinfo; + + dev_node = dev_of_node(&(pdev->dev)); + + struct resource *mem_dr; + struct resource *mem_gdir; + struct resource *mem_iomuxc_mux; + struct resource *mem_ccm_ccgrx; + struct resource *mem_iomux_pad; + struct resource t_mem_dr; + struct resource t_mem_gdir; + struct resource t_mem_iomuxc_mux; + struct resource t_mem_ccm_ccgrx; + struct resource t_mem_iomux_pad; + if(NULL == dev_node){ + printk(KERN_INFO"probe frome normal platform device\n"); + + led_hwinfo = dev_get_platdata(&(pdev->dev)); + + mem_dr = platform_get_resource(pdev,IORESOURCE_MEM,0); + mem_gdir = platform_get_resource(pdev,IORESOURCE_MEM,1); + mem_iomuxc_mux = platform_get_resource(pdev,IORESOURCE_MEM,2); + mem_ccm_ccgrx = platform_get_resource(pdev,IORESOURCE_MEM,3); + mem_iomux_pad = platform_get_resource(pdev,IORESOURCE_MEM,4); + } + else{ + printk(KERN_INFO"probe frome device tree\n"); + + led_hwinfo = devm_kzalloc(&(pdev->dev),2*sizeof(unsigned int), GFP_KERNEL); + if(!led_hwinfo){ + printk(KERN_ERR"devm_kzalloc failed\n"); + return -ENOMEM; + } + + if (0 != of_property_read_u32_array(dev_node, "hw_info", led_hwinfo, 2)){ + printk(KERN_ERR"read hw_info err\n"); + return -ENODATA; + } + + of_address_to_resource(dev_node,3,&t_mem_dr); + of_address_to_resource(dev_node,4,&t_mem_gdir); + of_address_to_resource(dev_node,1,&t_mem_iomuxc_mux); + of_address_to_resource(dev_node,0,&t_mem_ccm_ccgrx); + of_address_to_resource(dev_node,2,&t_mem_iomux_pad); + mem_dr = &t_mem_dr; + mem_gdir = &t_mem_gdir; + mem_iomuxc_mux = &t_mem_iomuxc_mux; + mem_ccm_ccgrx = &t_mem_ccm_ccgrx; + mem_iomux_pad = &t_mem_iomux_pad; + + if(0 != of_property_read_string(dev_node,"compatible",&compatible_str)){ + printk(KERN_ERR"read compatible err\n"); + return -ENODATA; + } + printk(KERN_INFO"compatible = %s\n",compatible_str); + if(0 == strcmp(compatible_str, "fire,rgb_led_red")){ + printk(KERN_INFO"red led\n"); + pdev->id = 0; + } + else if(0 == strcmp(compatible_str, "fire,rgb_led_green")){ + printk(KERN_INFO"green led\n"); + pdev->id = 1; + } + else{ + printk(KERN_INFO"blue led\n"); + pdev->id = 2; + } + } + + + my_led_dev = devm_kzalloc(&(pdev->dev),sizeof(struct led_chrdev), GFP_KERNEL); + if(!my_led_dev) + return -ENOMEM; + + my_led_dev->led_pin = led_hwinfo[0]; + my_led_dev->clock_offset = led_hwinfo[1]; + + my_led_dev->va_ccm_ccgrx = ioremap(mem_ccm_ccgrx->start,4); + my_led_dev->va_dr = ioremap(mem_dr->start,4); + my_led_dev->va_gdir = ioremap(mem_gdir->start,4); + my_led_dev->va_iomuxc_mux = ioremap(mem_iomuxc_mux->start,4); + my_led_dev->va_iomux_pad = ioremap(mem_iomux_pad->start,4); + + printk(KERN_INFO"hw_info:%u,%d\n",my_led_dev->led_pin,my_led_dev->clock_offset); + printk(KERN_INFO"reg:0x%x,0x%x,0x%x,0x%x,0x%x,\n",mem_ccm_ccgrx->start,\ + mem_dr->start,\ + mem_gdir->start,\ + mem_iomuxc_mux->start,\ + mem_iomux_pad->start); + + ret = alloc_chrdev_region(&devno, pdev->id, 1, "led"); + if(ret < 0){ + printk(KERN_ERR "chrdev alloc region failed\n"); + return ret; + } + printk(KERN_INFO "devno is %u(major:%u,minor:%u)\n",devno,MAJOR(devno), MINOR(devno)); + my_led_dev->my_devno = devno; + + cdev_init(&my_led_dev->dev, &led_fops); + my_led_dev->dev.owner = THIS_MODULE; + + ret = cdev_add(&my_led_dev->dev, devno, 1); + if(ret < 0){ + printk(KERN_ERR "chrdev add failed\n"); + goto add_err; + } + device_create(led_class,NULL,devno,NULL,"led_%s",(pdev->id == 0)?"red":((pdev->id == 1)? "green":"blue")); + + platform_set_drvdata(pdev, my_led_dev); + + return 0; + +add_err: + unregister_chrdev_region(my_led_dev, 1); + return ret; + +} + +int led_plat_remove(struct platform_device *pdev) +{ + dev_t cur_dev; + struct led_chrdev *my_led_dev = platform_get_drvdata(pdev); + + printk("led platform driver remove\n"); + + iounmap(my_led_dev->va_ccm_ccgrx); + iounmap(my_led_dev->va_dr); + iounmap(my_led_dev->va_gdir); + iounmap(my_led_dev->va_iomuxc_mux); + iounmap(my_led_dev->va_iomux_pad); + cur_dev = my_led_dev->my_devno; + cdev_del(&my_led_dev->dev); + device_destroy(led_class, cur_dev); + unregister_chrdev_region(cur_dev, 1); + + return 0; +} + +static const struct of_device_id rgb_led[] = { + {.compatible = "fire,rgb_led_red"}, + {.compatible = "fire,rgb_led_green"}, + {.compatible = "fire,rgb_led_blue"}, + {/* sentinel */} + }; + + +static struct platform_driver led_pdrv = { + .probe = led_plat_probe, + .remove = led_plat_remove, + .driver = { + .name = "led_pdev", + .owner = THIS_MODULE, + .of_match_table = rgb_led, + }, + .id_table = led_pdev_ids, +}; + + +static int __init led_plat_drv_init(void) +{ + printk("led platform driver init\n"); + + + led_class = class_create(THIS_MODULE, "led_class"); + if(IS_ERR(led_class)) + { + printk(KERN_ERR" create class faild!/n"); + return -EBUSY; + } + platform_driver_register(&led_pdrv); + + return 0; +} + +static void __exit led_plat_drv_exit(void) +{ + + printk("led platform driver exit\n"); + + platform_driver_unregister(&led_pdrv); + class_destroy(led_class); +} + + +module_init(led_plat_drv_init); +module_exit(led_plat_drv_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + + + diff --git a/examples/LICENSE b/examples/LICENSE new file mode 100644 index 0000000..c1ff8b6 --- /dev/null +++ b/examples/LICENSE @@ -0,0 +1,27 @@ + + +Unless otherwise stated, the source code distributed with this book +can be redistributed in source or binary form so long as an +acknowledgment appears in derived source files. The citation should +list that the code comes from BOOK by AUTHOR, published by O'Reilly & +Associates. This code is under copyright and cannot be included in +any other book, publication, or educational product without permission +from O'Reilly & Associates. No warranty is attached; we cannot take +responsibility for errors or fitness for use. + + +There are a few exception to this licence, however: a few sources +herein are distributed according to the GNU General Public License, +version 2. You'll find a copy of the license in +/usr/src/linux/COPYING, and in other places in your filesystem. The +affected source files are: + + pci/pci_skel.c + tty/tiny_serial.c + tty/tiny_tty.c + usb/usb-skeleton.c + +The files in ./pci ./tty and ./usb inherit the GPL from the kernel +sources, as most of their code comes straight from the kernel +(usb-skeleton.c being part of the kernel source tree directly.) + diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..e3e7fc2 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,12 @@ + +SUBDIRS = misc-progs misc-modules \ + skull scull scullc sculld scullp scullv sbull snull\ + short shortprint pci simple usb tty lddbus + +all: subdirs + +subdirs: + for n in $(SUBDIRS); do $(MAKE) -C $$n || exit 1; done + +clean: + for n in $(SUBDIRS); do $(MAKE) -C $$n clean; done diff --git a/examples/include/lddbus.h b/examples/include/lddbus.h new file mode 100644 index 0000000..a6b349e --- /dev/null +++ b/examples/include/lddbus.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +struct ldd_driver { + char *version; + struct module *module; + struct device_driver driver; + struct driver_attribute version_attr; +}; + +#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver); + +/* + * 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(dev) container_of(dev, 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 *); diff --git a/examples/lddbus/Makefile b/examples/lddbus/Makefile new file mode 100644 index 0000000..1a3ab78 --- /dev/null +++ b/examples/lddbus/Makefile @@ -0,0 +1,39 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif +CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR) + + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := lddbus.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) LDDINCDIR=$(PWD)/../include modules + +endif + + + +clean: + rm -rf *.o *.ko *~ core .depend *.mod.c .*.cmd .tmp_versions .*.o.d + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/lddbus/lddbus.c b/examples/lddbus/lddbus.c new file mode 100644 index 0000000..be37c6c --- /dev/null +++ b/examples/lddbus/lddbus.c @@ -0,0 +1,177 @@ +/* + * A virtual bus for LDD sample code devices to plug into. This + * code is heavily borrowed from drivers/base/sys.c + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + */ +/* $Id: lddbus.c,v 1.9 2004/09/26 08:12:27 gregkh Exp $ */ + +#include +#include +#include +#include +#include +#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_hotplug(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + envp[0] = buffer; + if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", + Version) >= buffer_size) + return -ENOMEM; + envp[1] = NULL; + return 0; +} + +/* + * Match LDD devices to drivers. Just do a simple name test. + */ +static int ldd_match(struct device *dev, struct device_driver *driver) +{ + return !strncmp(dev->bus_id, driver->name, strlen(driver->name)); +} + + +/* + * The LDD bus device. + */ +static void ldd_bus_release(struct device *dev) +{ + printk(KERN_DEBUG "lddbus release\n"); +} + +struct device ldd_bus = { + .bus_id = "ldd0", + .release = ldd_bus_release +}; + + +/* + * And the bus type. + */ +struct bus_type ldd_bus_type = { + .name = "ldd", + .match = ldd_match, + .hotplug = ldd_hotplug, +}; + +/* + * Export a simple attribute. + */ +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); + + + +/* + * LDD devices. + */ + +/* + * For now, no references to LDDbus devices go out which are not + * tracked via the module reference count, so we use a no-op + * release function. + */ +static void ldd_dev_release(struct device *dev) +{ } + +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; + strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE); + 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); + +/* + * Crude driver interface. + */ + + +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; + ret = driver_register(&driver->driver); + if (ret) + return ret; + driver->version_attr.attr.name = "version"; + driver->version_attr.attr.owner = driver->module; + 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) +{ + 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) +{ + device_unregister(&ldd_bus); + bus_unregister(&ldd_bus_type); +} + +module_init(ldd_bus_init); +module_exit(ldd_bus_exit); diff --git a/examples/misc-modules/Makefile b/examples/misc-modules/Makefile new file mode 100644 index 0000000..d04d2d6 --- /dev/null +++ b/examples/misc-modules/Makefile @@ -0,0 +1,32 @@ + +# To build modules outside of the kernel tree, we run "make" +# in the kernel source tree; the Makefile these then includes this +# Makefile once again. +# This conditional selects whether we are being included from the +# kernel Makefile or not. +ifeq ($(KERNELRELEASE),) + + # Assume the source tree is where the running kernel was built + # You should set KERNELDIR in the environment if it's elsewhere + KERNELDIR ?= /lib/modules/$(shell uname -r)/build + # The current directory is passed to sub-makes as argument + PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +modules_install: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +.PHONY: modules modules_install clean + +else + # called from kernel build system: just declare what our modules are + obj-m := hello.o hellop.o seq.o jit.o jiq.o sleepy.o complete.o \ + silly.o faulty.o kdatasize.o kdataalign.o +endif + + diff --git a/examples/misc-modules/complete.c b/examples/misc-modules/complete.c new file mode 100644 index 0000000..700923f --- /dev/null +++ b/examples/misc-modules/complete.c @@ -0,0 +1,81 @@ +/* + * complete.c -- the writers awake the readers + * + * Copyright (C) 2003 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2003 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: complete.c,v 1.2 2004/09/26 07:02:43 gregkh Exp $ + */ + +#include +#include + +#include /* current and everything */ +#include /* printk() */ +#include /* everything... */ +#include /* size_t */ +#include + +MODULE_LICENSE("Dual BSD/GPL"); + +static int complete_major = 0; + +DECLARE_COMPLETION(comp); + +ssize_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t *pos) +{ + printk(KERN_DEBUG "process %i (%s) going to sleep\n", + current->pid, current->comm); + wait_for_completion(&comp); + printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); + return 0; /* EOF */ +} + +ssize_t complete_write (struct file *filp, const char __user *buf, size_t count, + loff_t *pos) +{ + printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", + current->pid, current->comm); + complete(&comp); + return count; /* succeed, to avoid retrial */ +} + + +struct file_operations complete_fops = { + .owner = THIS_MODULE, + .read = complete_read, + .write = complete_write, +}; + + +int complete_init(void) +{ + int result; + + /* + * Register your major, and accept a dynamic number + */ + result = register_chrdev(complete_major, "complete", &complete_fops); + if (result < 0) + return result; + if (complete_major == 0) + complete_major = result; /* dynamic */ + return 0; +} + +void complete_cleanup(void) +{ + unregister_chrdev(complete_major, "complete"); +} + +module_init(complete_init); +module_exit(complete_cleanup); + diff --git a/examples/misc-modules/faulty.c b/examples/misc-modules/faulty.c new file mode 100644 index 0000000..48c1850 --- /dev/null +++ b/examples/misc-modules/faulty.c @@ -0,0 +1,89 @@ +/* + * faulty.c -- a module which generates an oops when read + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: faulty.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $ + */ + + +#include +#include +#include + +#include /* printk() */ +#include /* everything... */ +#include /* size_t */ +#include + +MODULE_LICENSE("Dual BSD/GPL"); + + +int faulty_major = 0; + +ssize_t faulty_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + char stack_buf[4]; + + /* Let's try a buffer overflow */ + memset(stack_buf, 0xff, 20); + if (count > 4) + count = 4; /* copy 4 bytes to the user */ + ret = copy_to_user(buf, stack_buf, count); + if (!ret) + return count; + return ret; +} + +ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count, + loff_t *pos) +{ + /* make a simple fault by dereferencing a NULL pointer */ + *(int *)0 = 0; + return 0; +} + + + +struct file_operations faulty_fops = { + .read = faulty_read, + .write = faulty_write, + .owner = THIS_MODULE +}; + + +int faulty_init(void) +{ + int result; + + /* + * Register your major, and accept a dynamic number + */ + result = register_chrdev(faulty_major, "faulty", &faulty_fops); + if (result < 0) + return result; + if (faulty_major == 0) + faulty_major = result; /* dynamic */ + + return 0; +} + +void faulty_cleanup(void) +{ + unregister_chrdev(faulty_major, "faulty"); +} + +module_init(faulty_init); +module_exit(faulty_cleanup); + diff --git a/examples/misc-modules/hello.c b/examples/misc-modules/hello.c new file mode 100644 index 0000000..85cd6d0 --- /dev/null +++ b/examples/misc-modules/hello.c @@ -0,0 +1,20 @@ +/* + * $Id: hello.c,v 1.5 2004/10/26 03:32:21 corbet Exp $ + */ +#include +#include +MODULE_LICENSE("Dual BSD/GPL"); + +static int hello_init(void) +{ + printk(KERN_ALERT "Hello, world\n"); + return 0; +} + +static void hello_exit(void) +{ + printk(KERN_ALERT "Goodbye, cruel world\n"); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/examples/misc-modules/hellop.c b/examples/misc-modules/hellop.c new file mode 100644 index 0000000..88eaa67 --- /dev/null +++ b/examples/misc-modules/hellop.c @@ -0,0 +1,40 @@ +/* + * $Id: hellop.c,v 1.4 2004/09/26 07:02:43 gregkh Exp $ + */ +#include +#include +#include + +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * These lines, although not shown in the book, + * are needed to make hello.c run properly even when + * your kernel has version support enabled + */ + + +/* + * A couple of parameters that can be passed in: how many times we say + * hello, and to whom. + */ +static char *whom = "world"; +static int howmany = 1; +module_param(howmany, int, S_IRUGO); +module_param(whom, charp, S_IRUGO); + +static int hello_init(void) +{ + int i; + for (i = 0; i < howmany; i++) + printk(KERN_ALERT "(%d) Hello, %s\n", i, whom); + return 0; +} + +static void hello_exit(void) +{ + printk(KERN_ALERT "Goodbye, cruel world\n"); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/examples/misc-modules/jiq.c b/examples/misc-modules/jiq.c new file mode 100644 index 0000000..dc7da28 --- /dev/null +++ b/examples/misc-modules/jiq.c @@ -0,0 +1,264 @@ +/* + * jiq.c -- the just-in-queue module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: jiq.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include /* everything... */ +#include +#include /* error codes */ +#include +#include +#include /* tasklets */ + +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * The delay for the delayed workqueue timer file. + */ +static long delay = 1; +module_param(delay, long, 0); + + +/* + * This module is a silly one: it only embeds short code fragments + * that show how enqueued tasks `feel' the environment + */ + +#define LIMIT (PAGE_SIZE-128) /* don't print any more after this size */ + +/* + * Print information about the current environment. This is called from + * within the task queues. If the limit is reched, awake the reading + * process. + */ +static DECLARE_WAIT_QUEUE_HEAD (jiq_wait); + + +static struct work_struct jiq_work; + + + +/* + * Keep track of info we need between task queue runs. + */ +static struct clientdata { + int len; + char *buf; + unsigned long jiffies; + long delay; +} jiq_data; + +#define SCHEDULER_QUEUE ((task_queue *) 1) + + + +static void jiq_print_tasklet(unsigned long); +static DECLARE_TASKLET(jiq_tasklet, jiq_print_tasklet, (unsigned long)&jiq_data); + + +/* + * Do the printing; return non-zero if the task should be rescheduled. + */ +static int jiq_print(void *ptr) +{ + struct clientdata *data = ptr; + int len = data->len; + char *buf = data->buf; + unsigned long j = jiffies; + + if (len > LIMIT) { + wake_up_interruptible(&jiq_wait); + return 0; + } + + if (len == 0) + len = sprintf(buf," time delta preempt pid cpu command\n"); + else + len =0; + + /* intr_count is only exported since 1.3.5, but 1.99.4 is needed anyways */ + len += sprintf(buf+len, "%9li %4li %3i %5i %3i %s\n", + j, j - data->jiffies, + preempt_count(), current->pid, smp_processor_id(), + current->comm); + + data->len += len; + data->buf += len; + data->jiffies = j; + return 1; +} + + +/* + * Call jiq_print from a work queue + */ +static void jiq_print_wq(void *ptr) +{ + struct clientdata *data = (struct clientdata *) ptr; + + if (! jiq_print (ptr)) + return; + + if (data->delay) + schedule_delayed_work(&jiq_work, data->delay); + else + schedule_work(&jiq_work); +} + + + +static int jiq_read_wq(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + DEFINE_WAIT(wait); + + jiq_data.len = 0; /* nothing printed, yet */ + jiq_data.buf = buf; /* print in this place */ + jiq_data.jiffies = jiffies; /* initial time */ + jiq_data.delay = 0; + + prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE); + schedule_work(&jiq_work); + schedule(); + finish_wait(&jiq_wait, &wait); + + *eof = 1; + return jiq_data.len; +} + + +static int jiq_read_wq_delayed(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + DEFINE_WAIT(wait); + + jiq_data.len = 0; /* nothing printed, yet */ + jiq_data.buf = buf; /* print in this place */ + jiq_data.jiffies = jiffies; /* initial time */ + jiq_data.delay = delay; + + prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE); + schedule_delayed_work(&jiq_work, delay); + schedule(); + finish_wait(&jiq_wait, &wait); + + *eof = 1; + return jiq_data.len; +} + + + + +/* + * Call jiq_print from a tasklet + */ +static void jiq_print_tasklet(unsigned long ptr) +{ + if (jiq_print ((void *) ptr)) + tasklet_schedule (&jiq_tasklet); +} + + + +static int jiq_read_tasklet(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + jiq_data.len = 0; /* nothing printed, yet */ + jiq_data.buf = buf; /* print in this place */ + jiq_data.jiffies = jiffies; /* initial time */ + + tasklet_schedule(&jiq_tasklet); + interruptible_sleep_on(&jiq_wait); /* sleep till completion */ + + *eof = 1; + return jiq_data.len; +} + + + + +/* + * This one, instead, tests out the timers. + */ + +static struct timer_list jiq_timer; + +static void jiq_timedout(unsigned long ptr) +{ + jiq_print((void *)ptr); /* print a line */ + wake_up_interruptible(&jiq_wait); /* awake the process */ +} + + +static int jiq_read_run_timer(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + + jiq_data.len = 0; /* prepare the argument for jiq_print() */ + jiq_data.buf = buf; + jiq_data.jiffies = jiffies; + + init_timer(&jiq_timer); /* init the timer structure */ + jiq_timer.function = jiq_timedout; + jiq_timer.data = (unsigned long)&jiq_data; + jiq_timer.expires = jiffies + HZ; /* one second */ + + jiq_print(&jiq_data); /* print and go to sleep */ + add_timer(&jiq_timer); + interruptible_sleep_on(&jiq_wait); /* RACE */ + del_timer_sync(&jiq_timer); /* in case a signal woke us up */ + + *eof = 1; + return jiq_data.len; +} + + + +/* + * the init/clean material + */ + +static int jiq_init(void) +{ + + /* this line is in jiq_init() */ + INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data); + + create_proc_read_entry("jiqwq", 0, NULL, jiq_read_wq, NULL); + create_proc_read_entry("jiqwqdelay", 0, NULL, jiq_read_wq_delayed, NULL); + create_proc_read_entry("jitimer", 0, NULL, jiq_read_run_timer, NULL); + create_proc_read_entry("jiqtasklet", 0, NULL, jiq_read_tasklet, NULL); + + return 0; /* succeed */ +} + +static void jiq_cleanup(void) +{ + remove_proc_entry("jiqwq", NULL); + remove_proc_entry("jiqwqdelay", NULL); + remove_proc_entry("jitimer", NULL); + remove_proc_entry("jiqtasklet", NULL); +} + + +module_init(jiq_init); +module_exit(jiq_cleanup); diff --git a/examples/misc-modules/jit.c b/examples/misc-modules/jit.c new file mode 100644 index 0000000..62978b0 --- /dev/null +++ b/examples/misc-modules/jit.c @@ -0,0 +1,292 @@ +/* + * jit.c -- the just-in-time module + * + * Copyright (C) 2001,2003 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001,2003 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: jit.c,v 1.16 2004/09/26 07:02:43 gregkh Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +/* + * This module is a silly one: it only embeds short code fragments + * that show how time delays can be handled in the kernel. + */ + +int delay = HZ; /* the default delay, expressed in jiffies */ + +module_param(delay, int, 0); + +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* use these as data pointers, to implement four files in one function */ +enum jit_files { + JIT_BUSY, + JIT_SCHED, + JIT_QUEUE, + JIT_SCHEDTO +}; + +/* + * This function prints one line of data, after sleeping one second. + * It can sleep in different ways, according to the data pointer + */ +int jit_fn(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + unsigned long j0, j1; /* jiffies */ + wait_queue_head_t wait; + + init_waitqueue_head (&wait); + j0 = jiffies; + j1 = j0 + delay; + + switch((long)data) { + case JIT_BUSY: + while (time_before(jiffies, j1)) + cpu_relax(); + break; + case JIT_SCHED: + while (time_before(jiffies, j1)) { + schedule(); + } + break; + case JIT_QUEUE: + wait_event_interruptible_timeout(wait, 0, delay); + break; + case JIT_SCHEDTO: + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout (delay); + break; + } + j1 = jiffies; /* actual value after we delayed */ + + len = sprintf(buf, "%9li %9li\n", j0, j1); + *start = buf; + return len; +} + +/* + * This file, on the other hand, returns the current time forever + */ +int jit_currentime(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + struct timeval tv1; + struct timespec tv2; + unsigned long j1; + u64 j2; + + /* get them four */ + j1 = jiffies; + j2 = get_jiffies_64(); + do_gettimeofday(&tv1); + tv2 = current_kernel_time(); + + /* print */ + len=0; + len += sprintf(buf,"0x%08lx 0x%016Lx %10i.%06i\n" + "%40i.%09i\n", + j1, j2, + (int) tv1.tv_sec, (int) tv1.tv_usec, + (int) tv2.tv_sec, (int) tv2.tv_nsec); + *start = buf; + return len; +} + +/* + * The timer example follows + */ + +int tdelay = 10; +module_param(tdelay, int, 0); + +/* This data structure used as "data" for the timer and tasklet functions */ +struct jit_data { + struct timer_list timer; + struct tasklet_struct tlet; + int hi; /* tasklet or tasklet_hi */ + wait_queue_head_t wait; + unsigned long prevjiffies; + unsigned char *buf; + int loops; +}; +#define JIT_ASYNC_LOOPS 5 + +void jit_timer_fn(unsigned long arg) +{ + struct jit_data *data = (struct jit_data *)arg; + unsigned long j = jiffies; + data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n", + j, j - data->prevjiffies, in_interrupt() ? 1 : 0, + current->pid, smp_processor_id(), current->comm); + + if (--data->loops) { + data->timer.expires += tdelay; + data->prevjiffies = j; + add_timer(&data->timer); + } else { + wake_up_interruptible(&data->wait); + } +} + +/* the /proc function: allocate everything to allow concurrency */ +int jit_timer(char *buf, char **start, off_t offset, + int len, int *eof, void *unused_data) +{ + struct jit_data *data; + char *buf2 = buf; + unsigned long j = jiffies; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + init_timer(&data->timer); + init_waitqueue_head (&data->wait); + + /* write the first lines in the buffer */ + buf2 += sprintf(buf2, " time delta inirq pid cpu command\n"); + buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n", + j, 0L, in_interrupt() ? 1 : 0, + current->pid, smp_processor_id(), current->comm); + + /* fill the data for our timer function */ + data->prevjiffies = j; + data->buf = buf2; + data->loops = JIT_ASYNC_LOOPS; + + /* register the timer */ + data->timer.data = (unsigned long)data; + data->timer.function = jit_timer_fn; + data->timer.expires = j + tdelay; /* parameter */ + add_timer(&data->timer); + + /* wait for the buffer to fill */ + wait_event_interruptible(data->wait, !data->loops); + if (signal_pending(current)) + return -ERESTARTSYS; + buf2 = data->buf; + kfree(data); + *eof = 1; + return buf2 - buf; +} + +void jit_tasklet_fn(unsigned long arg) +{ + struct jit_data *data = (struct jit_data *)arg; + unsigned long j = jiffies; + data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n", + j, j - data->prevjiffies, in_interrupt() ? 1 : 0, + current->pid, smp_processor_id(), current->comm); + + if (--data->loops) { + data->prevjiffies = j; + if (data->hi) + tasklet_hi_schedule(&data->tlet); + else + tasklet_schedule(&data->tlet); + } else { + wake_up_interruptible(&data->wait); + } +} + +/* the /proc function: allocate everything to allow concurrency */ +int jit_tasklet(char *buf, char **start, off_t offset, + int len, int *eof, void *arg) +{ + struct jit_data *data; + char *buf2 = buf; + unsigned long j = jiffies; + long hi = (long)arg; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + init_waitqueue_head (&data->wait); + + /* write the first lines in the buffer */ + buf2 += sprintf(buf2, " time delta inirq pid cpu command\n"); + buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n", + j, 0L, in_interrupt() ? 1 : 0, + current->pid, smp_processor_id(), current->comm); + + /* fill the data for our tasklet function */ + data->prevjiffies = j; + data->buf = buf2; + data->loops = JIT_ASYNC_LOOPS; + + /* register the tasklet */ + tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data); + data->hi = hi; + if (hi) + tasklet_hi_schedule(&data->tlet); + else + tasklet_schedule(&data->tlet); + + /* wait for the buffer to fill */ + wait_event_interruptible(data->wait, !data->loops); + + if (signal_pending(current)) + return -ERESTARTSYS; + buf2 = data->buf; + kfree(data); + *eof = 1; + return buf2 - buf; +} + + + +int __init jit_init(void) +{ + create_proc_read_entry("currentime", 0, NULL, jit_currentime, NULL); + create_proc_read_entry("jitbusy", 0, NULL, jit_fn, (void *)JIT_BUSY); + create_proc_read_entry("jitsched",0, NULL, jit_fn, (void *)JIT_SCHED); + create_proc_read_entry("jitqueue",0, NULL, jit_fn, (void *)JIT_QUEUE); + create_proc_read_entry("jitschedto", 0, NULL, jit_fn, (void *)JIT_SCHEDTO); + + create_proc_read_entry("jitimer", 0, NULL, jit_timer, NULL); + create_proc_read_entry("jitasklet", 0, NULL, jit_tasklet, NULL); + create_proc_read_entry("jitasklethi", 0, NULL, jit_tasklet, (void *)1); + + return 0; /* success */ +} + +void __exit jit_cleanup(void) +{ + remove_proc_entry("currentime", NULL); + remove_proc_entry("jitbusy", NULL); + remove_proc_entry("jitsched", NULL); + remove_proc_entry("jitqueue", NULL); + remove_proc_entry("jitschedto", NULL); + + remove_proc_entry("jitimer", NULL); + remove_proc_entry("jitasklet", NULL); + remove_proc_entry("jitasklethi", NULL); +} + +module_init(jit_init); +module_exit(jit_cleanup); diff --git a/examples/misc-modules/kdataalign.c b/examples/misc-modules/kdataalign.c new file mode 100644 index 0000000..92d8ace --- /dev/null +++ b/examples/misc-modules/kdataalign.c @@ -0,0 +1,69 @@ +/* + * kdatasize.c -- print the size of common data items from kernel space + * This runs with any Linux kernel (not any Unix, because of ) + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Define several data structures, all of them start with a lone char + * in order to present an unaligned offset for the next field + */ +struct c {char c; char t;} c; +struct s {char c; short t;} s; +struct i {char c; int t;} i; +struct l {char c; long t;} l; +struct ll {char c; long long t;} ll; +struct p {char c; void * t;} p; +struct u1b {char c; __u8 t;} u1b; +struct u2b {char c; __u16 t;} u2b; +struct u4b {char c; __u32 t;} u4b; +struct u8b {char c; __u64 t;} u8b; + +static void data_cleanup(void) +{ + /* never called */ +} + +static int data_init(void) +{ + /* print information and return an error */ + printk("arch Align: char short int long ptr long-long " + " u8 u16 u32 u64\n"); + printk("%-12s %3i %3i %3i %3i %3i %3i " + "%3i %3i %3i %3i\n", + system_utsname.machine, + /* note that gcc can subtract void * values, but it's not ansi */ + (int)((void *)(&c.t) - (void *)&c), + (int)((void *)(&s.t) - (void *)&s), + (int)((void *)(&i.t) - (void *)&i), + (int)((void *)(&l.t) - (void *)&l), + (int)((void *)(&p.t) - (void *)&p), + (int)((void *)(&ll.t) - (void *)&ll), + (int)((void *)(&u1b.t) - (void *)&u1b), + (int)((void *)(&u2b.t) - (void *)&u2b), + (int)((void *)(&u4b.t) - (void *)&u4b), + (int)((void *)(&u8b.t) - (void *)&u8b)); + return -ENODEV; +} + +module_init(data_init); +module_exit(data_cleanup); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/examples/misc-modules/kdatasize.c b/examples/misc-modules/kdatasize.c new file mode 100644 index 0000000..4ac8895 --- /dev/null +++ b/examples/misc-modules/kdatasize.c @@ -0,0 +1,48 @@ +/* + * kdatasize.c -- print the size of common data items from kernel space + * This runs with any Linux kernel (not any Unix, because of ) + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include + +#include +#include +#include +#include + +static void data_cleanup(void) +{ + /* never called */ +} + +int data_init(void) +{ + /* print information and return an error */ + printk("arch Size: char short int long ptr long-long " + " u8 u16 u32 u64\n"); + printk("%-12s %3i %3i %3i %3i %3i %3i " + "%3i %3i %3i %3i\n", + system_utsname.machine, + (int)sizeof(char), (int)sizeof(short), (int)sizeof(int), + (int)sizeof(long), + (int)sizeof(void *), (int)sizeof(long long), (int)sizeof(__u8), + (int)sizeof(__u16), (int)sizeof(__u32), (int)sizeof(__u64)); + return -ENODEV; +} + +module_init(data_init); +module_exit(data_cleanup); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/examples/misc-modules/seq.c b/examples/misc-modules/seq.c new file mode 100644 index 0000000..59026a5 --- /dev/null +++ b/examples/misc-modules/seq.c @@ -0,0 +1,109 @@ +/* + * Simple demonstration of the seq_file interface. + * + * $Id: seq.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $ + */ + +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Jonathan Corbet"); +MODULE_LICENSE("Dual BSD/GPL"); + + + +/* + * The sequence iterator functions. The position as seen by the + * filesystem is just the count that we return. + */ +static void *ct_seq_start(struct seq_file *s, loff_t *pos) +{ + loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL); + if (!spos) + return NULL; + *spos = *pos; + return spos; +} + +static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + loff_t *spos = (loff_t *) v; + *pos = ++(*spos); + return spos; +} + +static void ct_seq_stop(struct seq_file *s, void *v) +{ + kfree (v); +} + +/* + * The show function. + */ +static int ct_seq_show(struct seq_file *s, void *v) +{ + loff_t *spos = (loff_t *) v; + seq_printf(s, "%Ld\n", *spos); + return 0; +} + +/* + * Tie them all together into a set of seq_operations. + */ +static struct seq_operations ct_seq_ops = { + .start = ct_seq_start, + .next = ct_seq_next, + .stop = ct_seq_stop, + .show = ct_seq_show +}; + + +/* + * Time to set up the file operations for our /proc file. In this case, + * all we need is an open function which sets up the sequence ops. + */ + +static int ct_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ct_seq_ops); +}; + +/* + * The file operations structure contains our open function along with + * set of the canned seq_ ops. + */ +static struct file_operations ct_file_ops = { + .owner = THIS_MODULE, + .open = ct_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + + +/* + * Module setup and teardown. + */ + +static int ct_init(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_entry("sequence", 0, NULL); + if (entry) + entry->proc_fops = &ct_file_ops; + return 0; +} + +static void ct_exit(void) +{ + remove_proc_entry("sequence", NULL); +} + +module_init(ct_init); +module_exit(ct_exit); diff --git a/examples/misc-modules/silly.c b/examples/misc-modules/silly.c new file mode 100644 index 0000000..3b1f893 --- /dev/null +++ b/examples/misc-modules/silly.c @@ -0,0 +1,294 @@ +/* + * silly.c -- Simple Tool for Unloading and Printing ISA Data + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: silly.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $ + */ + +/* =========================> BIG FAT WARNING: + * This will only work on architectures with an ISA memory range. + * It won't work on other computers. + */ + +#include +#include +#include +#include + +#include +#include /* printk() */ +#include /* everything... */ +#include /* error codes */ +#include +#include +#include +#include + +#include +#include + +int silly_major = 0; +module_param(silly_major, int, 0); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * The devices access the 640k-1M memory. + * minor 0 uses ioread8/iowrite8 + * minor 1 uses ioread16/iowrite16 + * minor 2 uses ioread32/iowrite32 + * minor 3 uses memcpy_fromio()/memcpy_toio() + */ + +/* + * Here's our address range, and a place to store the ioremap'd base. + */ +#define ISA_BASE 0xA0000 +#define ISA_MAX 0x100000 /* for general memory access */ + +#define VIDEO_MAX 0xC0000 /* for vga access */ +#define VGA_BASE 0xb8000 +static void __iomem *io_base; + + + +int silly_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +int silly_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +enum silly_modes {M_8=0, M_16, M_32, M_memcpy}; + +ssize_t silly_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + int retval; + int mode = iminor(filp->f_dentry->d_inode); + void __iomem *add; + unsigned long isa_addr = ISA_BASE + *f_pos; + unsigned char *kbuf, *ptr; + + if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */ + count = ISA_MAX - isa_addr; + + /* + * too big an f_pos (caused by a malicious lseek()) + * would result in a negative count + */ + if (count < 0) + return 0; + + kbuf = kmalloc(count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + ptr = kbuf; + retval = count; + /* + * Convert our address into our remapped area. + */ + add = (void __iomem *)(io_base + (isa_addr - ISA_BASE)); + /* + * kbuf is aligned, but the reads might not. In order not to + * drive me mad with unaligned leading and trailing bytes, + * I downgrade the `mode' if unaligned xfers are requested. + */ + + if (mode == M_32 && ((isa_addr | count) & 3)) + mode = M_16; + if (mode == M_16 && ((isa_addr | count) & 1)) + mode = M_8; + + switch(mode) { + case M_32: + while (count >= 4) { + *(u32 *)ptr = ioread32(add); + add += 4; + count -= 4; + ptr += 4; + } + break; + + case M_16: + while (count >= 2) { + *(u16 *)ptr = ioread16(add); + add+=2; + count-=2; + ptr+=2; + } + break; + + case M_8: + while (count) { + *ptr = ioread8(add); + add++; + count--; + ptr++; + } + break; + + case M_memcpy: + memcpy_fromio(ptr, add, count); + break; + + default: + return -EINVAL; + } + if ((retval > 0) && copy_to_user(buf, kbuf, retval)) + retval = -EFAULT; + kfree(kbuf); + *f_pos += retval; + return retval; +} + + +ssize_t silly_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + int retval; + int mode = iminor(filp->f_dentry->d_inode); + unsigned long isa_addr = ISA_BASE + *f_pos; + unsigned char *kbuf, *ptr; + void __iomem *add; + + /* + * Writing is dangerous. + * Allow root-only, independently of device permissions + */ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */ + count = ISA_MAX - isa_addr; + + /* + * too big an f_pos (caused by a malicious lseek()) + * results in a negative count + */ + if (count < 0) + return 0; + + kbuf = kmalloc(count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + ptr = kbuf; + retval=count; + + /* + * kbuf is aligned, but the writes might not. In order not to + * drive me mad with unaligned leading and trailing bytes, + * I downgrade the `mode' if unaligned xfers are requested. + */ + + if (mode == M_32 && ((isa_addr | count) & 3)) + mode = M_16; + if (mode == M_16 && ((isa_addr | count) & 1)) + mode = M_8; + + if (copy_from_user(kbuf, buf, count)) { + kfree(kbuf); + return -EFAULT; + } + ptr = kbuf; + + /* + * Switch over to our remapped address space. + */ + add = (void __iomem *)(io_base + (isa_addr - ISA_BASE)); + + switch(mode) { + case M_32: + while (count >= 4) { + iowrite8(*(u32 *)ptr, add); + add += 4; + count -= 4; + ptr += 4; + } + break; + + case M_16: + while (count >= 2) { + iowrite8(*(u16 *)ptr, add); + add += 2; + count -= 2; + ptr += 2; + } + break; + + case M_8: + while (count) { + iowrite8(*ptr, add); + add++; + count--; + ptr++; + } + break; + + case M_memcpy: + memcpy_toio(add, ptr, count); + break; + + default: + return -EINVAL; + } + *f_pos += retval; + kfree(kbuf); + return retval; +} + + +unsigned int silly_poll(struct file *filp, poll_table *wait) +{ + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; +} + + +struct file_operations silly_fops = { + .read = silly_read, + .write = silly_write, + .poll = silly_poll, + .open = silly_open, + .release = silly_release, + .owner = THIS_MODULE +}; + +int silly_init(void) +{ + int result = register_chrdev(silly_major, "silly", &silly_fops); + if (result < 0) { + printk(KERN_INFO "silly: can't get major number\n"); + return result; + } + if (silly_major == 0) + silly_major = result; /* dynamic */ + /* + * Set up our I/O range. + */ + + /* this line appears in silly_init */ + io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE); + return 0; +} + +void silly_cleanup(void) +{ + iounmap(io_base); + unregister_chrdev(silly_major, "silly"); +} + + +module_init(silly_init); +module_exit(silly_cleanup); diff --git a/examples/misc-modules/sleepy.c b/examples/misc-modules/sleepy.c new file mode 100644 index 0000000..33c7fc3 --- /dev/null +++ b/examples/misc-modules/sleepy.c @@ -0,0 +1,84 @@ +/* + * sleepy.c -- the writers awake the readers + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: sleepy.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $ + */ + +#include +#include + +#include /* current and everything */ +#include /* printk() */ +#include /* everything... */ +#include /* size_t */ +#include + +MODULE_LICENSE("Dual BSD/GPL"); + +static int sleepy_major = 0; + +static DECLARE_WAIT_QUEUE_HEAD(wq); +static int flag = 0; + +ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos) +{ + printk(KERN_DEBUG "process %i (%s) going to sleep\n", + current->pid, current->comm); + wait_event_interruptible(wq, flag != 0); + flag = 0; + printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); + return 0; /* EOF */ +} + +ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count, + loff_t *pos) +{ + printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", + current->pid, current->comm); + flag = 1; + wake_up_interruptible(&wq); + return count; /* succeed, to avoid retrial */ +} + + +struct file_operations sleepy_fops = { + .owner = THIS_MODULE, + .read = sleepy_read, + .write = sleepy_write, +}; + + +int sleepy_init(void) +{ + int result; + + /* + * Register your major, and accept a dynamic number + */ + result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops); + if (result < 0) + return result; + if (sleepy_major == 0) + sleepy_major = result; /* dynamic */ + return 0; +} + +void sleepy_cleanup(void) +{ + unregister_chrdev(sleepy_major, "sleepy"); +} + +module_init(sleepy_init); +module_exit(sleepy_cleanup); + diff --git a/examples/misc-progs/Makefile b/examples/misc-progs/Makefile new file mode 100644 index 0000000..759ddec --- /dev/null +++ b/examples/misc-progs/Makefile @@ -0,0 +1,13 @@ + +FILES = nbtest load50 mapcmp polltest mapper setlevel setconsole inp outp \ + datasize dataalign netifdebug + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +INCLUDEDIR = $(KERNELDIR)/include +CFLAGS = -O2 -fomit-frame-pointer -Wall -I$(INCLUDEDIR) + +all: $(FILES) + +clean: + rm -f $(FILES) *~ core + diff --git a/examples/misc-progs/asynctest.c b/examples/misc-progs/asynctest.c new file mode 100644 index 0000000..8dcb458 --- /dev/null +++ b/examples/misc-progs/asynctest.c @@ -0,0 +1,57 @@ +/* + * asynctest.c: use async notification to read stdin + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include +#include +#include +#include +#include + +int gotdata=0; +void sighandler(int signo) +{ + if (signo==SIGIO) + gotdata++; + return; +} + +char buffer[4096]; + +int main(int argc, char **argv) +{ + int count; + struct sigaction action; + + memset(&action, 0, sizeof(action)); + action.sa_handler = sighandler; + action.sa_flags = 0; + + sigaction(SIGIO, &action, NULL); + + fcntl(STDIN_FILENO, F_SETOWN, getpid()); + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC); + + while(1) { + /* this only returns if a signal arrives */ + sleep(86400); /* one day */ + if (!gotdata) + continue; + count=read(0, buffer, 4096); + /* buggy: if avail data is more than 4kbytes... */ + write(1,buffer,count); + gotdata=0; + } +} diff --git a/examples/misc-progs/dataalign.c b/examples/misc-progs/dataalign.c new file mode 100644 index 0000000..1042ab7 --- /dev/null +++ b/examples/misc-progs/dataalign.c @@ -0,0 +1,58 @@ +/* + * dataalign.c -- show alignment needs + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * This runs with any Linux kernel (not any Unix, because of ) + */ +#include +#include +#include + +/* + * Define several data structures, all of them start with a lone char + * in order to present an unaligned offset for the next field + */ +struct c {char c; char t;} c; +struct s {char c; short t;} s; +struct i {char c; int t;} i; +struct l {char c; long t;} l; +struct ll {char c; long long t;} ll; +struct p {char c; void * t;} p; +struct u1b {char c; __u8 t;} u1b; +struct u2b {char c; __u16 t;} u2b; +struct u4b {char c; __u32 t;} u4b; +struct u8b {char c; __u64 t;} u8b; + +int main(int argc, char **argv) +{ + struct utsname name; + + uname(&name); /* never fails :) */ + printf("arch Align: char short int long ptr long-long " + " u8 u16 u32 u64\n"); + printf( "%-12s %3i %3i %3i %3i %3i %3i " + "%3i %3i %3i %3i\n", + name.machine, + /* note that gcc can subtract void * values, but it's not ansi */ + (int)((void *)(&c.t) - (void *)&c), + (int)((void *)(&s.t) - (void *)&s), + (int)((void *)(&i.t) - (void *)&i), + (int)((void *)(&l.t) - (void *)&l), + (int)((void *)(&p.t) - (void *)&p), + (int)((void *)(&ll.t) - (void *)&ll), + (int)((void *)(&u1b.t) - (void *)&u1b), + (int)((void *)(&u2b.t) - (void *)&u2b), + (int)((void *)(&u4b.t) - (void *)&u4b), + (int)((void *)(&u8b.t) - (void *)&u8b)); + return 0; +} diff --git a/examples/misc-progs/datasize.c b/examples/misc-progs/datasize.c new file mode 100644 index 0000000..bf63423 --- /dev/null +++ b/examples/misc-progs/datasize.c @@ -0,0 +1,35 @@ +/* + * datasize.c -- print the size of common data items + * This runs with any Linux kernel (not any Unix, because of ) + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ +#include +#include +#include + +int main(int argc, char **argv) +{ + struct utsname name; + + uname(&name); /* never fails :) */ + printf("arch Size: char short int long ptr long-long " + " u8 u16 u32 u64\n"); + printf( "%-12s %3i %3i %3i %3i %3i %3i " + "%3i %3i %3i %3i\n", + name.machine, + (int)sizeof(char), (int)sizeof(short), (int)sizeof(int), + (int)sizeof(long), + (int)sizeof(void *), (int)sizeof(long long), (int)sizeof(__u8), + (int)sizeof(__u16), (int)sizeof(__u32), (int)sizeof(__u64)); + return 0; +} diff --git a/examples/misc-progs/gdbline b/examples/misc-progs/gdbline new file mode 100644 index 0000000..d66572e --- /dev/null +++ b/examples/misc-progs/gdbline @@ -0,0 +1,19 @@ +#!/bin/bash +# +# $Id: gdbline,v 1.1 2004/08/02 16:27:55 corbet Exp $ +# +# gdbline module image +# +# Outputs an add-symbol-file line suitable for pasting into gdb to examine +# a loaded module. +# +cd /sys/module/$1/sections +echo -n add-symbol-file $2 `/bin/cat .text` + +for section in .[a-z]* *; do + if [ $section != ".text" ]; then + echo " \\" + echo -n " -s" $section `/bin/cat $section` + fi +done +echo diff --git a/examples/misc-progs/inp.c b/examples/misc-progs/inp.c new file mode 100644 index 0000000..282d570 --- /dev/null +++ b/examples/misc-progs/inp.c @@ -0,0 +1,129 @@ +/* + * inp.c -- read all the ports specified in hex on the command line. + * The program uses the faster ioperm/iopl calls on x86, /dev/port + * on other platforms. The program acts as inb/inw/inl according + * to its own name + * + * Copyright (C) 1998,2000,2001 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* linux-specific */ + +#ifdef __GLIBC__ +# include +#endif + +#define PORT_FILE "/dev/port" + +char *prgname; + +#ifdef __i386__ +static int read_and_print_one(unsigned int port,int size) +{ + static int iopldone = 0; + + if (port > 1024) { + if (!iopldone && iopl(3)) { + fprintf(stderr, "%s: iopl(): %s\n", prgname, strerror(errno)); + return 1; + } + iopldone++; + } else if (ioperm(port,size,1)) { + fprintf(stderr, "%s: ioperm(%x): %s\n", prgname, + port, strerror(errno)); + return 1; + } + + if (size == 4) + printf("%04x: %08x\n", port, inl(port)); + else if (size == 2) + printf("%04x: %04x\n", port, inw(port)); + else + printf("%04x: %02x\n", port, inb(port)); + return 0; +} +#else /* not i386 */ + +static int read_and_print_one(unsigned int port,int size) +{ + static int fd = -1; + unsigned char b; unsigned short w; unsigned int l; + + if (fd < 0) + fd = open(PORT_FILE, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", prgname, PORT_FILE, strerror(errno)); + return 1; + } + lseek(fd, port, SEEK_SET); + + if (size == 4) { + read(fd, &l, 4); + printf("%04x: 0x%08x\n", port, l); + } else if (size == 2) { + read(fd, &w, 2); + printf("%04x: 0x%04x\n", port, w & 0xffff); + } else { + read(fd, &b, 1); + printf("%04x: 0x%02x\n", port, b & 0xff); + } + return 0; +} + +#endif /* i386 */ + + +int main(int argc, char **argv) +{ + unsigned int i, n, port, size, error = 0; + + prgname = argv[0]; + /* find the data size */ + switch (prgname[strlen(prgname)-1]) { + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'b': case 'p': default: + size = 1; + } + + setuid(0); /* if we're setuid, force it on */ + for (i = 1; i < argc; i++) { + if ( sscanf(argv[i], "%x%n", &port, &n) < 1 + || n != strlen(argv[i]) ) { + fprintf(stderr, "%s: argument \"%s\" is not a hex number\n", + argv[0], argv[i]); + error++; continue; + } + if (port & (size-1)) { + fprintf(stderr, "%s: argument \"%s\" is not properly aligned\n", + argv[0], argv[i]); + error++; continue; + } + error += read_and_print_one(port, size); + } + exit (error ? 1 : 0); +} + diff --git a/examples/misc-progs/load50.c b/examples/misc-progs/load50.c new file mode 100644 index 0000000..f9d93a0 --- /dev/null +++ b/examples/misc-progs/load50.c @@ -0,0 +1,38 @@ +/* + * load50.c -- a simple busy-looping tool. + * Obviously, this runs with any kernel and any Unix + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include +#include + +int main(int argc, char **argv) +{ + int i, load=50; + + if (argc==2) { + load=atoi(argv[1]); + } + printf("Bringing load to %i\n",load); + + for (i=0; i +#include +#include +#include +#include +#include + +static char *mapdev (const char *, unsigned long, unsigned long); +#define PAGE_SIZE 4096 + +/* + * memcmp dev1 dev2 offset pages + */ +int main (int argc, char **argv) +{ + unsigned long offset, size, i; + char *addr1, *addr2; +/* + * Sanity check. + */ + if (argc != 5) + { + fprintf (stderr, "Usage: mapcmp dev1 dev2 offset pages\n"); + exit (1); + } +/* + * Map the two devices. + */ + offset = strtoul (argv[3], NULL, 16); + size = atoi (argv[4])*PAGE_SIZE; + printf ("Offset is 0x%lx\n", offset); + addr1 = mapdev (argv[1], offset, size); + addr2 = mapdev (argv[2], offset, size); +/* + * Do the comparison. + */ + printf ("Comparing..."); + fflush (stdout); + for (i = 0; i < size; i++) + if (*addr1++ != *addr2++) + { + printf ("areas differ at byte %ld\n", i); + exit (0); + } + printf ("areas are identical.\n"); + exit (0); +} + + + +static char *mapdev (const char *dev, unsigned long offset, + unsigned long size) +{ + char *addr; + int fd = open (dev, O_RDONLY); + + if (fd < 0) + { + perror (dev); + exit (1); + } + addr = mmap (0, size, PROT_READ, MAP_PRIVATE, fd, offset); + if (addr == MAP_FAILED) + { + perror (dev); + exit (1); + } + printf ("Mapped %s (%lu @ %lx) at %p\n", dev, size, offset, addr); + return (addr); +} diff --git a/examples/misc-progs/mapper.c b/examples/misc-progs/mapper.c new file mode 100644 index 0000000..ae8db09 --- /dev/null +++ b/examples/misc-progs/mapper.c @@ -0,0 +1,71 @@ +/* + * mapper.c -- simple file that mmap()s a file region and prints it + * + * Copyright (C) 1998,2000,2001 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char *fname; + FILE *f; + unsigned long offset, len; + void *address; + + if (argc !=4 + || sscanf(argv[2],"%li", &offset) != 1 + || sscanf(argv[3],"%li", &len) != 1) { + fprintf(stderr, "%s: Usage \"%s \"\n", argv[0], + argv[0]); + exit(1); + } + /* the offset might be big (e.g., PCI devices), but conversion trims it */ + if (offset == INT_MAX) { + if (argv[2][1]=='x') + sscanf(argv[2]+2, "%lx", &offset); + else + sscanf(argv[2], "%lu", &offset); + } + + fname=argv[1]; + + if (!(f=fopen(fname,"r"))) { + fprintf(stderr, "%s: %s: %s\n", argv[0], fname, strerror(errno)); + exit(1); + } + + address=mmap(0, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fileno(f), offset); + + if (address == (void *)-1) { + fprintf(stderr,"%s: mmap(): %s\n",argv[0],strerror(errno)); + exit(1); + } + fclose(f); + fprintf(stderr, "mapped \"%s\" from %lu (0x%08lx) to %lu (0x%08lx)\n", + fname, offset, offset, offset+len, offset+len); + + fwrite(address, 1, len, stdout); + return 0; +} + diff --git a/examples/misc-progs/nbtest.c b/examples/misc-progs/nbtest.c new file mode 100644 index 0000000..6a0bd54 --- /dev/null +++ b/examples/misc-progs/nbtest.c @@ -0,0 +1,44 @@ +/* + * nbtest.c: read and write in non-blocking mode + * This should run with any Unix + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include +#include +#include +#include + +char buffer[4096]; + +int main(int argc, char **argv) +{ + int delay = 1, n, m = 0; + + if (argc > 1) + delay=atoi(argv[1]); + fcntl(0, F_SETFL, fcntl(0,F_GETFL) | O_NONBLOCK); /* stdin */ + fcntl(1, F_SETFL, fcntl(1,F_GETFL) | O_NONBLOCK); /* stdout */ + + while (1) { + n = read(0, buffer, 4096); + if (n >= 0) + m = write(1, buffer, n); + if ((n < 0 || m < 0) && (errno != EAGAIN)) + break; + sleep(delay); + } + perror(n < 0 ? "stdin" : "stdout"); + exit(1); +} diff --git a/examples/misc-progs/netifdebug.c b/examples/misc-progs/netifdebug.c new file mode 100644 index 0000000..6f0ce75 --- /dev/null +++ b/examples/misc-progs/netifdebug.c @@ -0,0 +1,84 @@ +/* + * netifdebug.c -- change the IFF_DEBUG flag of an interface + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int action = -1, sock; + struct ifreq req; + char *actname; + + if (argc < 2) { + fprintf(stderr,"%s: usage is \"%s []\"\n", + argv[0],argv[0]); + exit(1); + } + if (argc==2) + actname="tell"; + else + actname=argv[2]; + + /* a silly raw socket just for ioctl()ling it */ + sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (sock < 0) { + fprintf(stderr, "%s: socket(): %s\n", argv[0],strerror(errno)); + exit(1); + } + + /* retrieve flags */ + strcpy(req.ifr_name, argv[1]); + if ( ioctl(sock, SIOCGIFFLAGS, &req) < 0) { + fprintf(stderr, " %s: ioctl(SIOCGIFFLAGS): %s\n", + argv[0],strerror(errno)); + exit(1); + } + + if (!strcmp(actname,"on") + || !strcmp(actname,"+") + || !strcmp(actname,"1")) + action = IFF_DEBUG; + + if (!strcmp(actname,"off") + || !strcmp(actname,"-") + || !strcmp(actname,"0")) + action = 0; + + if (!strcmp(actname,"tell") + || actname[0]=='t') { + printf("%s: debug is %s\n", argv[1], + req.ifr_flags & IFF_DEBUG ? "on" : "off"); + exit(0); + } + + req.ifr_flags &= ~IFF_DEBUG; + req.ifr_flags |= action; + + if ( ioctl(sock, SIOCSIFFLAGS, &req) < 0) { + fprintf(stderr, " %s: ioctl(SIOCSIFFLAGS): %s\n", + argv[0],strerror(errno)); + exit(1); + } + exit(0); +} diff --git a/examples/misc-progs/outp.c b/examples/misc-progs/outp.c new file mode 100644 index 0000000..219d613 --- /dev/null +++ b/examples/misc-progs/outp.c @@ -0,0 +1,136 @@ +/* + * outp.c -- write all the ports specified in hex on the command line. + * The program uses the faster ioperm/iopl calls on x86, /dev/port + * on other platforms. The program acts as outb/outw/outl according + * to its own name + * + * Copyright (C) 1998,2000,2001 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* linux-specific */ + +#ifdef __GLIBC__ +# include +#endif + +#define PORT_FILE "/dev/port" + +char *prgname; + +#ifdef __i386__ +static int write_one(unsigned int port, unsigned int val, int size) +{ + static int iopldone = 0; + + if (port > 1024) { + if (!iopldone && iopl(3)) { + fprintf(stderr, "%s: iopl(): %s\n", prgname, strerror(errno)); + return 1; + } + iopldone++; + } else if (ioperm(port,size,1)) { + fprintf(stderr, "%s: ioperm(%x): %s\n", prgname, + port, strerror(errno)); + return 1; + } + + if (size == 4) + outl(val, port); + else if (size == 2) + outw(val&0xffff, port); + else + outb(val&0xff, port); + return 0; +} +#else /* not i386 */ + +static int write_one(unsigned int port, unsigned int val, int size) +{ + static int fd = -1; + unsigned char b; unsigned short w; + + if (fd < 0) + fd = open(PORT_FILE, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", prgname, PORT_FILE, strerror(errno)); + return 1; + } + lseek(fd, port, SEEK_SET); + + if (size == 4) { + write(fd, &val, 4); + } else if (size == 2) { + w = val; + write(fd, &w, 2); + } else { + b = val; + write(fd, &b, 1); + } + return 0; +} + +#endif /* i386 */ + +int main(int argc, char **argv) +{ + unsigned int i, n, port, val, size, error = 0; + + prgname = argv[0]; + /* find the data size */ + switch (prgname[strlen(prgname)-1]) { + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'b': case 'p': default: + size = 1; + } + setuid(0); /* if we're setuid, force it on */ + for (i=1;i (size == 1 ? 0xff : 0xffff)) { + fprintf(stderr, "%s: argument \"%s\" out of range\n", + argv[0], argv[i+1]); + error++; continue; + } + error += write_one(port, val, size); + } + exit (error ? 1 : 0); +} diff --git a/examples/misc-progs/polltest.c b/examples/misc-progs/polltest.c new file mode 100644 index 0000000..fdc461d --- /dev/null +++ b/examples/misc-progs/polltest.c @@ -0,0 +1,47 @@ +/* + * Test out reading with poll() + * This should run with any Unix + * + * Copyright (C) 2003 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2003 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: polltest.c,v 1.1 2003/02/07 18:01:38 corbet Exp $ + */ + +#include +#include +#include +#include +#include +#include + +char buffer[4096]; + +int main(int argc, char **argv) +{ + struct pollfd pfd; + int n; + + fcntl(0, F_SETFL, fcntl(0,F_GETFL) | O_NONBLOCK); /* stdin */ + pfd.fd = 0; /* stdin */ + pfd.events = POLLIN; + + while (1) { + n=read(0, buffer, 4096); + if (n >= 0) + write(1, buffer, n); + n = poll(&pfd, 1, -1); + if (n < 0) + break; + } + perror( n<0 ? "stdin" : "stdout"); + exit(1); +} diff --git a/examples/misc-progs/setconsole.c b/examples/misc-progs/setconsole.c new file mode 100644 index 0000000..04bc11d --- /dev/null +++ b/examples/misc-progs/setconsole.c @@ -0,0 +1,42 @@ +/* + * setconsole.c -- choose a console to receive kernel messages + * + * Copyright (C) 1998,2000,2001 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char bytes[2] = {11,0}; /* 11 is the TIOCLINUX cmd number */ + + if (argc==2) bytes[1] = atoi(argv[1]); /* the chosen console */ + else { + fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1); + } + if (ioctl(STDIN_FILENO, TIOCLINUX, bytes)<0) { /* use stdin */ + fprintf(stderr,"%s: ioctl(stdin, TIOCLINUX): %s\n", + argv[0], strerror(errno)); + exit(1); + } + exit(0); +} diff --git a/examples/misc-progs/setlevel.c b/examples/misc-progs/setlevel.c new file mode 100644 index 0000000..fec666d --- /dev/null +++ b/examples/misc-progs/setlevel.c @@ -0,0 +1,47 @@ +/* + * setlevel.c -- choose a console_loglevel for the kernel + * + * Copyright (C) 1998,2000,2001 Alessandro Rubini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +/* #include */ /* conflicting on the alpha */ +#define __LIBRARY__ /* _syscall3 and friends are only available through this */ +#include + +/* define the system call, to override the library function */ +_syscall3(int, syslog, int, type, char *, bufp, int, len); + +int main(int argc, char **argv) +{ + int level; + + if (argc==2) { + level = atoi(argv[1]); /* the chosen console */ + } else { + fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1); + } + if (syslog(8,NULL,level) < 0) { + fprintf(stderr,"%s: syslog(setlevel): %s\n", + argv[0],strerror(errno)); + exit(1); + } + exit(0); +} diff --git a/examples/pci/Makefile b/examples/pci/Makefile new file mode 100644 index 0000000..8b6a333 --- /dev/null +++ b/examples/pci/Makefile @@ -0,0 +1,11 @@ +obj-m := pci_skel.o + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + diff --git a/examples/pci/pci_skel.c b/examples/pci/pci_skel.c new file mode 100644 index 0000000..f115669 --- /dev/null +++ b/examples/pci/pci_skel.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + + +static struct pci_device_id ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ids); + +static unsigned char skel_get_revision(struct pci_dev *dev) +{ + u8 revision; + + pci_read_config_byte(dev, PCI_REVISION_ID, &revision); + return revision; +} + +static int probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + /* Do probing type stuff here. + * Like calling request_region(); + */ + pci_enable_device(dev); + + if (skel_get_revision(dev) == 0x42) + return -ENODEV; + + + return 0; +} + +static void remove(struct pci_dev *dev) +{ + /* clean up any allocated resources and stuff here. + * like call release_region(); + */ +} + +static struct pci_driver pci_driver = { + .name = "pci_skel", + .id_table = ids, + .probe = probe, + .remove = remove, +}; + +static int __init pci_skel_init(void) +{ + return pci_register_driver(&pci_driver); +} + +static void __exit pci_skel_exit(void) +{ + pci_unregister_driver(&pci_driver); +} + +MODULE_LICENSE("GPL"); + +module_init(pci_skel_init); +module_exit(pci_skel_exit); diff --git a/examples/sbull/Makefile b/examples/sbull/Makefile new file mode 100644 index 0000000..5bb807c --- /dev/null +++ b/examples/sbull/Makefile @@ -0,0 +1,41 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) +CFLAGS += -I.. + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := sbull.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/sbull/sbull.c b/examples/sbull/sbull.c new file mode 100644 index 0000000..1a040c3 --- /dev/null +++ b/examples/sbull/sbull.c @@ -0,0 +1,456 @@ +/* + * Sample disk driver, from the beginning. + */ + +#include +#include +#include +#include + +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include +#include /* size_t */ +#include /* O_ACCMODE */ +#include /* HDIO_GETGEO */ +#include +#include +#include +#include +#include /* invalidate_bdev */ +#include + +MODULE_LICENSE("Dual BSD/GPL"); + +static int sbull_major = 0; +module_param(sbull_major, int, 0); +static int hardsect_size = 512; +module_param(hardsect_size, int, 0); +static int nsectors = 1024; /* How big the drive is */ +module_param(nsectors, int, 0); +static int ndevices = 4; +module_param(ndevices, int, 0); + +/* + * The different "request modes" we can use. + */ +enum { + RM_SIMPLE = 0, /* The extra-simple request function */ + RM_FULL = 1, /* The full-blown version */ + RM_NOQUEUE = 2, /* Use make_request */ +}; +static int request_mode = RM_SIMPLE; +module_param(request_mode, int, 0); + +/* + * Minor number and partition management. + */ +#define SBULL_MINORS 16 +#define MINOR_SHIFT 4 +#define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT + +/* + * We can tweak our hardware sector size, but the kernel talks to us + * in terms of small sectors, always. + */ +#define KERNEL_SECTOR_SIZE 512 + +/* + * After this much idle time, the driver will simulate a media change. + */ +#define INVALIDATE_DELAY 30*HZ + +/* + * The internal representation of our device. + */ +struct sbull_dev { + int size; /* Device size in sectors */ + u8 *data; /* The data array */ + short users; /* How many users */ + short media_change; /* Flag a media change? */ + spinlock_t lock; /* For mutual exclusion */ + struct request_queue *queue; /* The device request queue */ + struct gendisk *gd; /* The gendisk structure */ + struct timer_list timer; /* For simulated media changes */ +}; + +static struct sbull_dev *Devices = NULL; + +/* + * Handle an I/O request. + */ +static void sbull_transfer(struct sbull_dev *dev, unsigned long sector, + unsigned long nsect, char *buffer, int write) +{ + unsigned long offset = sector*KERNEL_SECTOR_SIZE; + unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE; + + if ((offset + nbytes) > dev->size) { + printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes); + return; + } + if (write) + memcpy(dev->data + offset, buffer, nbytes); + else + memcpy(buffer, dev->data + offset, nbytes); +} + +/* + * The simple form of the request function. + */ +static void sbull_request(request_queue_t *q) +{ + struct request *req; + + while ((req = elv_next_request(q)) != NULL) { + struct sbull_dev *dev = req->rq_disk->private_data; + if (! blk_fs_request(req)) { + printk (KERN_NOTICE "Skip non-fs request\n"); + end_request(req, 0); + continue; + } + // printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n", + // dev - Devices, rq_data_dir(req), + // req->sector, req->current_nr_sectors, + // req->flags); + sbull_transfer(dev, req->sector, req->current_nr_sectors, + req->buffer, rq_data_dir(req)); + end_request(req, 1); + } +} + + +/* + * Transfer a single BIO. + */ +static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio) +{ + int i; + struct bio_vec *bvec; + sector_t sector = bio->bi_sector; + + /* Do each segment independently. */ + bio_for_each_segment(bvec, bio, i) { + char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); + sbull_transfer(dev, sector, bio_cur_sectors(bio), + buffer, bio_data_dir(bio) == WRITE); + sector += bio_cur_sectors(bio); + __bio_kunmap_atomic(bio, KM_USER0); + } + return 0; /* Always "succeed" */ +} + +/* + * Transfer a full request. + */ +static int sbull_xfer_request(struct sbull_dev *dev, struct request *req) +{ + struct bio *bio; + int nsect = 0; + + rq_for_each_bio(bio, req) { + sbull_xfer_bio(dev, bio); + nsect += bio->bi_size/KERNEL_SECTOR_SIZE; + } + return nsect; +} + + + +/* + * Smarter request function that "handles clustering". + */ +static void sbull_full_request(request_queue_t *q) +{ + struct request *req; + int sectors_xferred; + struct sbull_dev *dev = q->queuedata; + + while ((req = elv_next_request(q)) != NULL) { + if (! blk_fs_request(req)) { + printk (KERN_NOTICE "Skip non-fs request\n"); + end_request(req, 0); + continue; + } + sectors_xferred = sbull_xfer_request(dev, req); + if (! end_that_request_first(req, 1, sectors_xferred)) { + blkdev_dequeue_request(req); + end_that_request_last(req); + } + } +} + + + +/* + * The direct make request version. + */ +static int sbull_make_request(request_queue_t *q, struct bio *bio) +{ + struct sbull_dev *dev = q->queuedata; + int status; + + status = sbull_xfer_bio(dev, bio); + bio_endio(bio, bio->bi_size, status); + return 0; +} + + +/* + * Open and close. + */ + +static int sbull_open(struct inode *inode, struct file *filp) +{ + struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; + + del_timer_sync(&dev->timer); + filp->private_data = dev; + spin_lock(&dev->lock); + if (! dev->users) + check_disk_change(inode->i_bdev); + dev->users++; + spin_unlock(&dev->lock); + return 0; +} + +static int sbull_release(struct inode *inode, struct file *filp) +{ + struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data; + + spin_lock(&dev->lock); + dev->users--; + + if (!dev->users) { + dev->timer.expires = jiffies + INVALIDATE_DELAY; + add_timer(&dev->timer); + } + spin_unlock(&dev->lock); + + return 0; +} + +/* + * Look for a (simulated) media change. + */ +int sbull_media_changed(struct gendisk *gd) +{ + struct sbull_dev *dev = gd->private_data; + + return dev->media_change; +} + +/* + * Revalidate. WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking + * with open. That needs to be reevaluated. + */ +int sbull_revalidate(struct gendisk *gd) +{ + struct sbull_dev *dev = gd->private_data; + + if (dev->media_change) { + dev->media_change = 0; + memset (dev->data, 0, dev->size); + } + return 0; +} + +/* + * The "invalidate" function runs out of the device timer; it sets + * a flag to simulate the removal of the media. + */ +void sbull_invalidate(unsigned long ldev) +{ + struct sbull_dev *dev = (struct sbull_dev *) ldev; + + spin_lock(&dev->lock); + if (dev->users || !dev->data) + printk (KERN_WARNING "sbull: timer sanity check failed\n"); + else + dev->media_change = 1; + spin_unlock(&dev->lock); +} + +/* + * The ioctl() implementation + */ + +int sbull_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + long size; + struct hd_geometry geo; + struct sbull_dev *dev = filp->private_data; + + switch(cmd) { + case HDIO_GETGEO: + /* + * Get geometry: since we are a virtual device, we have to make + * up something plausible. So we claim 16 sectors, four heads, + * and calculate the corresponding number of cylinders. We set the + * start of data at sector four. + */ + size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE); + geo.cylinders = (size & ~0x3f) >> 6; + geo.heads = 4; + geo.sectors = 16; + geo.start = 4; + if (copy_to_user((void __user *) arg, &geo, sizeof(geo))) + return -EFAULT; + return 0; + } + + return -ENOTTY; /* unknown command */ +} + + + +/* + * The device operations structure. + */ +static struct block_device_operations sbull_ops = { + .owner = THIS_MODULE, + .open = sbull_open, + .release = sbull_release, + .media_changed = sbull_media_changed, + .revalidate_disk = sbull_revalidate, + .ioctl = sbull_ioctl +}; + + +/* + * Set up our internal device. + */ +static void setup_device(struct sbull_dev *dev, int which) +{ + /* + * Get some memory. + */ + memset (dev, 0, sizeof (struct sbull_dev)); + dev->size = nsectors*hardsect_size; + dev->data = vmalloc(dev->size); + if (dev->data == NULL) { + printk (KERN_NOTICE "vmalloc failure.\n"); + return; + } + spin_lock_init(&dev->lock); + + /* + * The timer which "invalidates" the device. + */ + init_timer(&dev->timer); + dev->timer.data = (unsigned long) dev; + dev->timer.function = sbull_invalidate; + + /* + * The I/O queue, depending on whether we are using our own + * make_request function or not. + */ + switch (request_mode) { + case RM_NOQUEUE: + dev->queue = blk_alloc_queue(GFP_KERNEL); + if (dev->queue == NULL) + goto out_vfree; + blk_queue_make_request(dev->queue, sbull_make_request); + break; + + case RM_FULL: + dev->queue = blk_init_queue(sbull_full_request, &dev->lock); + if (dev->queue == NULL) + goto out_vfree; + break; + + default: + printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode); + /* fall into.. */ + + case RM_SIMPLE: + dev->queue = blk_init_queue(sbull_request, &dev->lock); + if (dev->queue == NULL) + goto out_vfree; + break; + } + blk_queue_hardsect_size(dev->queue, hardsect_size); + dev->queue->queuedata = dev; + /* + * And the gendisk structure. + */ + dev->gd = alloc_disk(SBULL_MINORS); + if (! dev->gd) { + printk (KERN_NOTICE "alloc_disk failure\n"); + goto out_vfree; + } + dev->gd->major = sbull_major; + dev->gd->first_minor = which*SBULL_MINORS; + dev->gd->fops = &sbull_ops; + dev->gd->queue = dev->queue; + dev->gd->private_data = dev; + snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a'); + set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); + add_disk(dev->gd); + return; + + out_vfree: + if (dev->data) + vfree(dev->data); +} + + + +static int __init sbull_init(void) +{ + int i; + /* + * Get registered. + */ + sbull_major = register_blkdev(sbull_major, "sbull"); + if (sbull_major <= 0) { + printk(KERN_WARNING "sbull: unable to get major number\n"); + return -EBUSY; + } + /* + * Allocate the device array, and initialize each one. + */ + Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL); + if (Devices == NULL) + goto out_unregister; + for (i = 0; i < ndevices; i++) + setup_device(Devices + i, i); + + return 0; + + out_unregister: + unregister_blkdev(sbull_major, "sbd"); + return -ENOMEM; +} + +static void sbull_exit(void) +{ + int i; + + for (i = 0; i < ndevices; i++) { + struct sbull_dev *dev = Devices + i; + + del_timer_sync(&dev->timer); + if (dev->gd) { + del_gendisk(dev->gd); + put_disk(dev->gd); + } + if (dev->queue) { + if (request_mode == RM_NOQUEUE) + blk_put_queue(dev->queue); + else + blk_cleanup_queue(dev->queue); + } + if (dev->data) + vfree(dev->data); + } + unregister_blkdev(sbull_major, "sbull"); + kfree(Devices); +} + +module_init(sbull_init); +module_exit(sbull_exit); diff --git a/examples/sbull/sbull.h b/examples/sbull/sbull.h new file mode 100644 index 0000000..c443d70 --- /dev/null +++ b/examples/sbull/sbull.h @@ -0,0 +1,71 @@ + +/* + * sbull.h -- definitions for the char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + */ + + +#include + +/* Multiqueue only works on 2.4 */ +#ifdef SBULL_MULTIQUEUE +# warning "Multiqueue only works on 2.4 kernels" +#endif + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SBULL_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "sbull: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + + +#define SBULL_MAJOR 0 /* dynamic major by default */ +#define SBULL_DEVS 2 /* two disks */ +#define SBULL_RAHEAD 2 /* two sectors */ +#define SBULL_SIZE 2048 /* two megs each */ +#define SBULL_BLKSIZE 1024 /* 1k blocks */ +#define SBULL_HARDSECT 512 /* 2.2 and 2.4 can used different values */ + +#define SBULLR_MAJOR 0 /* Dynamic major for raw device */ +/* + * The sbull device is removable: if it is left closed for more than + * half a minute, it is removed. Thus use a usage count and a + * kernel timer + */ + +typedef struct Sbull_Dev { + int size; + int usage; + struct timer_list timer; + spinlock_t lock; + u8 *data; +#ifdef SBULL_MULTIQUEUE + request_queue_t *queue; + int busy; +#endif +} Sbull_Dev; diff --git a/examples/sbull/sbull_load b/examples/sbull/sbull_load new file mode 100644 index 0000000..8cdec56 --- /dev/null +++ b/examples/sbull/sbull_load @@ -0,0 +1,47 @@ +#!/bin/sh + +function make_minors { + let part=1 + while (($part < $minors)); do + let minor=$part+$2 + mknod $1$part b $major $minor + let part=$part+1 + done +} + + +# FIXME: This isn't handling minors (partitions) at all. +module="sbull" +device="sbull" +mode="664" +chardevice="sbullr" +minors=16 + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +# Remove stale nodes and replace them, then give gid and perms + +rm -f /dev/${device}[a-d]* /dev/${device} + +mknod /dev/${device}a b $major 0 +make_minors /dev/${device}a 0 +mknod /dev/${device}b b $major 16 +make_minors /dev/${device}b 16 +mknod /dev/${device}c b $major 32 +make_minors /dev/${device}c 32 +mknod /dev/${device}d b $major 48 +make_minors /dev/${device}d 48 +ln -sf ${device}a /dev/${device} +chgrp $group /dev/${device}[a-d]* +chmod $mode /dev/${device}[a-d]* diff --git a/examples/sbull/sbull_unload b/examples/sbull/sbull_unload new file mode 100644 index 0000000..aa22dfb --- /dev/null +++ b/examples/sbull/sbull_unload @@ -0,0 +1,14 @@ +#!/bin/sh +module="sbull" +device="sbull" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes +rm -f /dev/${device}[a-d]* /dev/${device} + + + + + diff --git a/examples/scull/.depend b/examples/scull/.depend new file mode 100644 index 0000000..e69de29 diff --git a/examples/scull/.main.o.d b/examples/scull/.main.o.d new file mode 100644 index 0000000..f30631c --- /dev/null +++ b/examples/scull/.main.o.d @@ -0,0 +1,230 @@ +main.o: /home/chao/code/module/examples/scull/main.c \ + include/linux/kconfig.h include/generated/autoconf.h \ + include/linux/compiler_types.h include/linux/compiler_attributes.h \ + include/linux/compiler-gcc.h include/linux/module.h include/linux/list.h \ + include/linux/types.h include/uapi/linux/types.h \ + arch/x86/include/generated/uapi/asm/types.h \ + include/uapi/asm-generic/types.h include/asm-generic/int-ll64.h \ + include/uapi/asm-generic/int-ll64.h \ + arch/x86/include/uapi/asm/bitsperlong.h \ + include/asm-generic/bitsperlong.h include/uapi/asm-generic/bitsperlong.h \ + include/uapi/linux/posix_types.h include/linux/stddef.h \ + include/uapi/linux/stddef.h include/linux/compiler_types.h \ + arch/x86/include/asm/posix_types.h \ + arch/x86/include/uapi/asm/posix_types_64.h \ + include/uapi/asm-generic/posix_types.h include/linux/poison.h \ + include/linux/const.h include/vdso/const.h include/uapi/linux/const.h \ + include/linux/kernel.h include/linux/limits.h \ + include/uapi/linux/limits.h include/vdso/limits.h \ + include/linux/linkage.h include/linux/stringify.h include/linux/export.h \ + include/linux/compiler.h arch/x86/include/asm/barrier.h \ + arch/x86/include/asm/alternative.h arch/x86/include/asm/asm.h \ + arch/x86/include/asm/nops.h include/asm-generic/barrier.h \ + include/linux/kasan-checks.h include/linux/kcsan-checks.h \ + arch/x86/include/asm/linkage.h include/linux/bitops.h \ + include/linux/bits.h include/vdso/bits.h include/linux/build_bug.h \ + arch/x86/include/asm/bitops.h arch/x86/include/asm/rmwcc.h \ + include/asm-generic/bitops/find.h include/asm-generic/bitops/sched.h \ + arch/x86/include/asm/arch_hweight.h arch/x86/include/asm/cpufeatures.h \ + arch/x86/include/asm/required-features.h \ + arch/x86/include/asm/disabled-features.h \ + include/asm-generic/bitops/const_hweight.h \ + include/asm-generic/bitops/instrumented-atomic.h \ + include/linux/instrumented.h \ + include/asm-generic/bitops/instrumented-non-atomic.h \ + include/asm-generic/bitops/instrumented-lock.h \ + include/asm-generic/bitops/le.h arch/x86/include/uapi/asm/byteorder.h \ + include/linux/byteorder/little_endian.h \ + include/uapi/linux/byteorder/little_endian.h include/linux/swab.h \ + include/uapi/linux/swab.h arch/x86/include/uapi/asm/swab.h \ + include/linux/byteorder/generic.h \ + include/asm-generic/bitops/ext2-atomic-setbit.h include/linux/log2.h \ + include/linux/typecheck.h include/linux/printk.h include/linux/init.h \ + include/linux/kern_levels.h include/linux/cache.h \ + include/uapi/linux/kernel.h include/uapi/linux/sysinfo.h \ + arch/x86/include/asm/cache.h include/linux/dynamic_debug.h \ + include/linux/jump_label.h arch/x86/include/asm/jump_label.h \ + arch/x86/include/asm/div64.h include/asm-generic/div64.h \ + include/linux/stat.h arch/x86/include/uapi/asm/stat.h \ + include/uapi/linux/stat.h include/linux/time.h include/linux/seqlock.h \ + include/linux/spinlock.h include/linux/preempt.h \ + arch/x86/include/asm/preempt.h arch/x86/include/asm/percpu.h \ + include/asm-generic/percpu.h include/linux/threads.h \ + include/linux/percpu-defs.h include/linux/thread_info.h \ + include/linux/bug.h arch/x86/include/asm/bug.h include/asm-generic/bug.h \ + include/linux/restart_block.h include/linux/time64.h \ + include/linux/math64.h include/vdso/math64.h include/vdso/time64.h \ + include/uapi/linux/time.h include/uapi/linux/time_types.h \ + arch/x86/include/asm/current.h arch/x86/include/asm/thread_info.h \ + arch/x86/include/asm/page.h arch/x86/include/asm/page_types.h \ + include/linux/mem_encrypt.h arch/x86/include/asm/mem_encrypt.h \ + arch/x86/include/uapi/asm/bootparam.h include/linux/screen_info.h \ + include/uapi/linux/screen_info.h include/linux/apm_bios.h \ + include/uapi/linux/apm_bios.h include/uapi/linux/ioctl.h \ + arch/x86/include/generated/uapi/asm/ioctl.h include/asm-generic/ioctl.h \ + include/uapi/asm-generic/ioctl.h include/linux/edd.h \ + include/uapi/linux/edd.h arch/x86/include/asm/ist.h \ + arch/x86/include/uapi/asm/ist.h include/video/edid.h \ + include/uapi/video/edid.h arch/x86/include/asm/page_64_types.h \ + arch/x86/include/asm/kaslr.h arch/x86/include/asm/page_64.h \ + include/linux/range.h include/asm-generic/memory_model.h \ + include/linux/pfn.h include/asm-generic/getorder.h \ + arch/x86/include/asm/cpufeature.h arch/x86/include/asm/processor.h \ + arch/x86/include/asm/processor-flags.h \ + arch/x86/include/uapi/asm/processor-flags.h \ + arch/x86/include/asm/math_emu.h arch/x86/include/asm/ptrace.h \ + arch/x86/include/asm/segment.h arch/x86/include/uapi/asm/ptrace.h \ + arch/x86/include/uapi/asm/ptrace-abi.h \ + arch/x86/include/asm/paravirt_types.h arch/x86/include/asm/desc_defs.h \ + arch/x86/include/asm/kmap_types.h include/asm-generic/kmap_types.h \ + arch/x86/include/asm/pgtable_types.h \ + arch/x86/include/asm/pgtable_64_types.h arch/x86/include/asm/sparsemem.h \ + arch/x86/include/asm/nospec-branch.h include/linux/static_key.h \ + include/linux/frame.h arch/x86/include/asm/alternative-asm.h \ + arch/x86/include/asm/msr-index.h arch/x86/include/asm/unwind_hints.h \ + arch/x86/include/asm/orc_types.h arch/x86/include/asm/spinlock_types.h \ + include/asm-generic/qspinlock_types.h \ + include/asm-generic/qrwlock_types.h \ + arch/x86/include/uapi/asm/sigcontext.h arch/x86/include/asm/msr.h \ + arch/x86/include/asm/msr-index.h \ + arch/x86/include/generated/uapi/asm/errno.h \ + include/uapi/asm-generic/errno.h include/uapi/asm-generic/errno-base.h \ + arch/x86/include/asm/cpumask.h include/linux/cpumask.h \ + include/linux/bitmap.h include/linux/string.h \ + include/uapi/linux/string.h arch/x86/include/asm/string.h \ + arch/x86/include/asm/string_64.h include/linux/atomic.h \ + arch/x86/include/asm/atomic.h arch/x86/include/asm/cmpxchg.h \ + arch/x86/include/asm/cmpxchg_64.h arch/x86/include/asm/atomic64_64.h \ + include/linux/atomic-arch-fallback.h \ + include/asm-generic/atomic-instrumented.h \ + include/asm-generic/atomic-long.h arch/x86/include/uapi/asm/msr.h \ + include/linux/tracepoint-defs.h arch/x86/include/asm/paravirt.h \ + arch/x86/include/asm/frame.h arch/x86/include/asm/special_insns.h \ + include/linux/irqflags.h arch/x86/include/asm/irqflags.h \ + arch/x86/include/asm/fpu/types.h arch/x86/include/asm/vmxfeatures.h \ + arch/x86/include/asm/vdso/processor.h include/linux/personality.h \ + include/uapi/linux/personality.h include/linux/err.h \ + include/linux/bottom_half.h arch/x86/include/generated/asm/mmiowb.h \ + include/asm-generic/mmiowb.h include/linux/spinlock_types.h \ + include/linux/lockdep.h include/linux/rwlock_types.h \ + arch/x86/include/asm/spinlock.h arch/x86/include/asm/qspinlock.h \ + include/asm-generic/qspinlock.h arch/x86/include/asm/qrwlock.h \ + include/asm-generic/qrwlock.h include/linux/rwlock.h \ + include/linux/spinlock_api_smp.h include/linux/rwlock_api_smp.h \ + include/linux/time32.h include/linux/timex.h include/uapi/linux/timex.h \ + include/uapi/linux/param.h arch/x86/include/generated/uapi/asm/param.h \ + include/asm-generic/param.h include/uapi/asm-generic/param.h \ + arch/x86/include/asm/timex.h arch/x86/include/asm/tsc.h \ + include/vdso/time32.h include/vdso/time.h include/linux/uidgid.h \ + include/linux/highuid.h include/linux/kmod.h include/linux/umh.h \ + include/linux/gfp.h include/linux/mmdebug.h include/linux/mmzone.h \ + include/linux/wait.h include/uapi/linux/wait.h include/linux/numa.h \ + include/linux/nodemask.h include/linux/pageblock-flags.h \ + include/linux/page-flags-layout.h include/generated/bounds.h \ + include/linux/mm_types.h include/linux/mm_types_task.h \ + arch/x86/include/asm/tlbbatch.h include/linux/auxvec.h \ + include/uapi/linux/auxvec.h arch/x86/include/uapi/asm/auxvec.h \ + include/linux/rbtree.h include/linux/rcupdate.h include/linux/rcutree.h \ + include/linux/rwsem.h include/linux/osq_lock.h \ + include/linux/completion.h include/linux/swait.h include/linux/uprobes.h \ + include/linux/errno.h include/uapi/linux/errno.h \ + arch/x86/include/asm/uprobes.h include/linux/notifier.h \ + include/linux/mutex.h include/linux/debug_locks.h include/linux/srcu.h \ + include/linux/workqueue.h include/linux/timer.h include/linux/ktime.h \ + include/linux/jiffies.h include/vdso/jiffies.h \ + include/generated/timeconst.h include/vdso/ktime.h \ + include/linux/timekeeping.h include/linux/timekeeping32.h \ + include/linux/debugobjects.h include/linux/rcu_segcblist.h \ + include/linux/srcutree.h include/linux/rcu_node_tree.h \ + arch/x86/include/asm/mmu.h include/linux/page-flags.h \ + include/linux/memory_hotplug.h arch/x86/include/asm/mmzone.h \ + arch/x86/include/asm/mmzone_64.h arch/x86/include/asm/smp.h \ + arch/x86/include/asm/mpspec.h arch/x86/include/asm/mpspec_def.h \ + arch/x86/include/asm/x86_init.h arch/x86/include/asm/apicdef.h \ + arch/x86/include/asm/apic.h arch/x86/include/asm/fixmap.h \ + arch/x86/include/asm/acpi.h include/acpi/pdc_intel.h \ + arch/x86/include/asm/numa.h arch/x86/include/asm/topology.h \ + include/asm-generic/topology.h arch/x86/include/uapi/asm/vsyscall.h \ + include/asm-generic/fixmap.h arch/x86/include/asm/hardirq.h \ + arch/x86/include/asm/io_apic.h arch/x86/include/asm/irq_vectors.h \ + include/linux/topology.h include/linux/arch_topology.h \ + include/linux/percpu.h include/linux/smp.h include/linux/smp_types.h \ + include/linux/llist.h include/linux/sysctl.h include/uapi/linux/sysctl.h \ + include/linux/elf.h arch/x86/include/asm/elf.h \ + arch/x86/include/asm/user.h arch/x86/include/asm/user_64.h \ + arch/x86/include/asm/fsgsbase.h arch/x86/include/asm/vdso.h \ + include/uapi/linux/elf.h include/uapi/linux/elf-em.h \ + include/linux/kobject.h include/linux/sysfs.h include/linux/kernfs.h \ + include/linux/idr.h include/linux/radix-tree.h include/linux/xarray.h \ + include/linux/kconfig.h include/linux/kobject_ns.h include/linux/kref.h \ + include/linux/refcount.h include/linux/moduleparam.h \ + include/linux/rbtree_latch.h include/linux/error-injection.h \ + include/asm-generic/error-injection.h arch/x86/include/asm/module.h \ + include/asm-generic/module.h arch/x86/include/asm/orc_types.h \ + include/linux/slab.h include/linux/overflow.h \ + include/linux/percpu-refcount.h include/linux/kasan.h include/linux/fs.h \ + include/linux/wait_bit.h include/linux/kdev_t.h \ + include/uapi/linux/kdev_t.h include/linux/dcache.h \ + include/linux/rculist.h include/linux/rculist_bl.h \ + include/linux/list_bl.h include/linux/bit_spinlock.h \ + include/linux/lockref.h include/linux/stringhash.h include/linux/hash.h \ + include/linux/path.h include/linux/list_lru.h include/linux/shrinker.h \ + include/linux/pid.h include/linux/capability.h \ + include/uapi/linux/capability.h include/linux/semaphore.h \ + include/linux/fcntl.h include/uapi/linux/fcntl.h \ + arch/x86/include/generated/uapi/asm/fcntl.h \ + include/uapi/asm-generic/fcntl.h include/uapi/linux/openat2.h \ + include/linux/migrate_mode.h include/linux/percpu-rwsem.h \ + include/linux/rcuwait.h include/linux/sched/signal.h \ + include/linux/signal.h include/linux/signal_types.h \ + include/uapi/linux/signal.h arch/x86/include/asm/signal.h \ + arch/x86/include/uapi/asm/signal.h \ + include/uapi/asm-generic/signal-defs.h \ + arch/x86/include/uapi/asm/siginfo.h include/uapi/asm-generic/siginfo.h \ + include/linux/sched.h include/uapi/linux/sched.h include/linux/sem.h \ + include/uapi/linux/sem.h include/linux/ipc.h \ + include/linux/rhashtable-types.h include/uapi/linux/ipc.h \ + arch/x86/include/generated/uapi/asm/ipcbuf.h \ + include/uapi/asm-generic/ipcbuf.h arch/x86/include/uapi/asm/sembuf.h \ + include/linux/shm.h include/uapi/linux/shm.h \ + include/uapi/asm-generic/hugetlb_encode.h \ + arch/x86/include/uapi/asm/shmbuf.h include/uapi/asm-generic/shmbuf.h \ + arch/x86/include/asm/shmparam.h include/linux/kcov.h \ + include/uapi/linux/kcov.h include/linux/plist.h include/linux/hrtimer.h \ + include/linux/hrtimer_defs.h include/linux/timerqueue.h \ + include/linux/seccomp.h include/uapi/linux/seccomp.h \ + arch/x86/include/asm/seccomp.h arch/x86/include/asm/unistd.h \ + arch/x86/include/uapi/asm/unistd.h \ + arch/x86/include/generated/uapi/asm/unistd_64.h \ + arch/x86/include/generated/asm/unistd_64_x32.h \ + arch/x86/include/generated/asm/unistd_32_ia32.h \ + arch/x86/include/asm/ia32_unistd.h include/asm-generic/seccomp.h \ + include/uapi/linux/unistd.h include/linux/resource.h \ + include/uapi/linux/resource.h \ + arch/x86/include/generated/uapi/asm/resource.h \ + include/asm-generic/resource.h include/uapi/asm-generic/resource.h \ + include/linux/latencytop.h include/linux/sched/prio.h \ + include/linux/sched/types.h include/linux/task_io_accounting.h \ + include/linux/posix-timers.h include/linux/alarmtimer.h \ + include/uapi/linux/rseq.h include/linux/kcsan.h \ + include/linux/sched/jobctl.h include/linux/sched/task.h \ + include/linux/uaccess.h arch/x86/include/asm/uaccess.h \ + arch/x86/include/asm/smap.h arch/x86/include/asm/extable.h \ + arch/x86/include/asm/uaccess_64.h include/linux/cred.h \ + include/linux/key.h include/linux/assoc_array.h \ + include/linux/sched/user.h include/linux/ratelimit.h \ + include/linux/rcu_sync.h include/linux/delayed_call.h \ + include/linux/uuid.h include/uapi/linux/uuid.h include/linux/errseq.h \ + include/linux/ioprio.h include/linux/sched/rt.h \ + include/linux/iocontext.h include/linux/fs_types.h \ + include/uapi/linux/fs.h include/linux/quota.h \ + include/linux/percpu_counter.h include/uapi/linux/dqblk_xfs.h \ + include/linux/dqblk_v1.h include/linux/dqblk_v2.h \ + include/linux/dqblk_qtree.h include/linux/projid.h \ + include/uapi/linux/quota.h include/linux/nfs_fs_i.h \ + include/linux/proc_fs.h include/linux/seq_file.h include/linux/cdev.h \ + include/linux/device.h include/linux/dev_printk.h include/linux/ioport.h \ + include/linux/klist.h include/linux/pm.h include/linux/device/bus.h \ + include/linux/device/class.h include/linux/device/driver.h \ + arch/x86/include/asm/device.h include/linux/pm_wakeup.h \ + /home/chao/code/module/examples/scull/scull.h diff --git a/examples/scull/.vscode/c_cpp_properties.json b/examples/scull/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..ba06c78 --- /dev/null +++ b/examples/scull/.vscode/c_cpp_properties.json @@ -0,0 +1,35 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu11", + "cppStandard": "gnu++14", + "intelliSenseMode": "clang-x64" + }, + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu11", + "cppStandard": "gnu++14", + "intelliSenseMode": "gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/examples/scull/.vscode/settings.json b/examples/scull/.vscode/settings.json new file mode 100644 index 0000000..691a8f6 --- /dev/null +++ b/examples/scull/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "C_Cpp.errorSquiggles": "Disabled" +} \ No newline at end of file diff --git a/examples/scull/Makefile b/examples/scull/Makefile new file mode 100644 index 0000000..ac6383a --- /dev/null +++ b/examples/scull/Makefile @@ -0,0 +1,43 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +EXTRA_CFLAGS += $(DEBFLAGS) +EXTRA_CFLAGS += -I$(LDDINC) + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +scull-objs := main.o pipe.o access.o + +obj-m := scull.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules + +endif + + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(EXTRA_CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/scull/Module.symvers b/examples/scull/Module.symvers new file mode 100644 index 0000000..e69de29 diff --git a/examples/scull/access.c b/examples/scull/access.c new file mode 100644 index 0000000..e2aebba --- /dev/null +++ b/examples/scull/access.c @@ -0,0 +1,417 @@ +/* + * access.c -- the files with access control on open + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: access.c,v 1.17 2004/09/26 07:29:56 gregkh Exp $ + */ + +/* FIXME: cloned devices as a use for kobjects? */ + +#include /* printk() */ +#include +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include +#include +#include +#include + +#include + +#include "scull.h" /* local definitions */ + +static dev_t scull_a_firstdev; /* Where our range begins */ + +/* + * These devices fall back on the main scull operations. They only + * differ in the implementation of open() and close() + */ + + + +/************************************************************************ + * + * The first device is the single-open one, + * it has an hw structure and an open count + */ + +static struct scull_dev scull_s_device; +static atomic_t scull_s_available = ATOMIC_INIT(1); + +static int scull_s_open(struct inode *inode, struct file *filp) +{ + struct scull_dev *dev = &scull_s_device; /* device information */ + + if (! atomic_dec_and_test (&scull_s_available)) { + atomic_inc(&scull_s_available); + return -EBUSY; /* already open */ + } + + /* then, everything else is copied from the bare scull device */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) + scull_trim(dev); + filp->private_data = dev; + return 0; /* success */ +} + +static int scull_s_release(struct inode *inode, struct file *filp) +{ + atomic_inc(&scull_s_available); /* release the device */ + return 0; +} + + +/* + * The other operations for the single-open device come from the bare device + */ +struct file_operations scull_sngl_fops = { + .owner = THIS_MODULE, + .llseek = scull_llseek, + .read = scull_read, + .write = scull_write, + .unlocked_ioctl = scull_ioctl, + .open = scull_s_open, + .release = scull_s_release, +}; + + +/************************************************************************ + * + * Next, the "uid" device. It can be opened multiple times by the + * same user, but access is denied to other users if the device is open + */ + +static struct scull_dev scull_u_device; +static int scull_u_count; /* initialized to 0 by default */ +static uid_t scull_u_owner; /* initialized to 0 by default */ +//static spinlock_t scull_u_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(scull_u_lock); + +static int scull_u_open(struct inode *inode, struct file *filp) +{ + struct scull_dev *dev = &scull_u_device; /* device information */ + + spin_lock(&scull_u_lock); + if (scull_u_count && + (scull_u_owner != current->cred->uid.val) && /* allow user */ + (scull_u_owner != current->cred->euid.val) && /* allow whoever did su */ + !capable(CAP_DAC_OVERRIDE)) { /* still allow root */ + spin_unlock(&scull_u_lock); + return -EBUSY; /* -EPERM would confuse the user */ + } + + if (scull_u_count == 0) + scull_u_owner = current->cred->uid.val; /* grab it */ + + scull_u_count++; + spin_unlock(&scull_u_lock); + +/* then, everything else is copied from the bare scull device */ + + if ((filp->f_flags & O_ACCMODE) == O_WRONLY) + scull_trim(dev); + filp->private_data = dev; + return 0; /* success */ +} + +static int scull_u_release(struct inode *inode, struct file *filp) +{ + spin_lock(&scull_u_lock); + scull_u_count--; /* nothing else */ + spin_unlock(&scull_u_lock); + return 0; +} + + + +/* + * The other operations for the device come from the bare device + */ +struct file_operations scull_user_fops = { + .owner = THIS_MODULE, + .llseek = scull_llseek, + .read = scull_read, + .write = scull_write, + .unlocked_ioctl = scull_ioctl, + .open = scull_u_open, + .release = scull_u_release, +}; + + +/************************************************************************ + * + * Next, the device with blocking-open based on uid + */ + +static struct scull_dev scull_w_device; +static int scull_w_count; /* initialized to 0 by default */ +static uid_t scull_w_owner; /* initialized to 0 by default */ +static DECLARE_WAIT_QUEUE_HEAD(scull_w_wait); +//static spinlock_t scull_w_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(scull_w_lock); + +static inline int scull_w_available(void) +{ + return scull_w_count == 0 || + scull_w_owner == current->cred->uid.val || + scull_w_owner == current->cred->euid.val || + capable(CAP_DAC_OVERRIDE); +} + + +static int scull_w_open(struct inode *inode, struct file *filp) +{ + struct scull_dev *dev = &scull_w_device; /* device information */ + + spin_lock(&scull_w_lock); + while (! scull_w_available()) { + spin_unlock(&scull_w_lock); + if (filp->f_flags & O_NONBLOCK) return -EAGAIN; + if (wait_event_interruptible (scull_w_wait, scull_w_available())) + return -ERESTARTSYS; /* tell the fs layer to handle it */ + spin_lock(&scull_w_lock); + } + if (scull_w_count == 0) + scull_w_owner = current->cred->uid.val; /* grab it */ + scull_w_count++; + spin_unlock(&scull_w_lock); + + /* then, everything else is copied from the bare scull device */ + if ((filp->f_flags & O_ACCMODE) == O_WRONLY) + scull_trim(dev); + filp->private_data = dev; + return 0; /* success */ +} + +static int scull_w_release(struct inode *inode, struct file *filp) +{ + int temp; + + spin_lock(&scull_w_lock); + scull_w_count--; + temp = scull_w_count; + spin_unlock(&scull_w_lock); + + if (temp == 0) + wake_up_interruptible_sync(&scull_w_wait); /* awake other uid's */ + return 0; +} + + +/* + * The other operations for the device come from the bare device + */ +struct file_operations scull_wusr_fops = { + .owner = THIS_MODULE, + .llseek = scull_llseek, + .read = scull_read, + .write = scull_write, + .unlocked_ioctl = scull_ioctl, + .open = scull_w_open, + .release = scull_w_release, +}; + +/************************************************************************ + * + * Finally the `cloned' private device. This is trickier because it + * involves list management, and dynamic allocation. + */ + +/* The clone-specific data structure includes a key field */ + +struct scull_listitem { + struct scull_dev device; + dev_t key; + struct list_head list; + +}; + +/* The list of devices, and a lock to protect it */ +static LIST_HEAD(scull_c_list); +//static spinlock_t scull_c_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(scull_c_lock); + +/* A placeholder scull_dev which really just holds the cdev stuff. */ +static struct scull_dev scull_c_device; + +/* Look for a device or create one if missing */ +static struct scull_dev *scull_c_lookfor_device(dev_t key) +{ + struct scull_listitem *lptr; + + list_for_each_entry(lptr, &scull_c_list, list) { + if (lptr->key == key) + return &(lptr->device); + } + + /* not found */ + lptr = kmalloc(sizeof(struct scull_listitem), GFP_KERNEL); + if (!lptr) + return NULL; + + /* initialize the device */ + memset(lptr, 0, sizeof(struct scull_listitem)); + lptr->key = key; + scull_trim(&(lptr->device)); /* initialize it */ + //init_MUTEX(&(lptr->device.sem)); + sema_init(&(lptr->device.sem),1); + + /* place it in the list */ + list_add(&lptr->list, &scull_c_list); + + return &(lptr->device); +} + +static int scull_c_open(struct inode *inode, struct file *filp) +{ + struct scull_dev *dev; + dev_t key; + + if (!current->signal->tty) { + PDEBUG("Process \"%s\" has no ctl tty\n", current->comm); + return -EINVAL; + } + key = tty_devnum(current->signal->tty); + + /* look for a scullc device in the list */ + spin_lock(&scull_c_lock); + dev = scull_c_lookfor_device(key); + spin_unlock(&scull_c_lock); + + if (!dev) + return -ENOMEM; + + /* then, everything else is copied from the bare scull device */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) + scull_trim(dev); + filp->private_data = dev; + return 0; /* success */ +} + +static int scull_c_release(struct inode *inode, struct file *filp) +{ + /* + * Nothing to do, because the device is persistent. + * A `real' cloned device should be freed on last close + */ + return 0; +} + + + +/* + * The other operations for the device come from the bare device + */ +struct file_operations scull_priv_fops = { + .owner = THIS_MODULE, + .llseek = scull_llseek, + .read = scull_read, + .write = scull_write, + .unlocked_ioctl = scull_ioctl, + .open = scull_c_open, + .release = scull_c_release, +}; + +/************************************************************************ + * + * And the init and cleanup functions come last + */ + +static struct scull_adev_info { + char *name; + struct scull_dev *sculldev; + struct file_operations *fops; +} scull_access_devs[] = { + { "scullsingle", &scull_s_device, &scull_sngl_fops }, + { "sculluid", &scull_u_device, &scull_user_fops }, + { "scullwuid", &scull_w_device, &scull_wusr_fops }, + { "sullpriv", &scull_c_device, &scull_priv_fops } +}; +#define SCULL_N_ADEVS 4 + +/* + * Set up a single device. + */ +static void scull_access_setup (dev_t devno, struct scull_adev_info *devinfo) +{ + struct scull_dev *dev = devinfo->sculldev; + int err; + + /* Initialize the device structure */ + dev->quantum = scull_quantum; + dev->qset = scull_qset; + //init_MUTEX(&dev->sem); + sema_init(&dev->sem,1); + + /* Do the cdev stuff. */ + cdev_init(&dev->cdev, devinfo->fops); + kobject_set_name(&dev->cdev.kobj, devinfo->name); + dev->cdev.owner = THIS_MODULE; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) { + printk(KERN_NOTICE "Error %d adding %s\n", err, devinfo->name); + kobject_put(&dev->cdev.kobj); + } else + printk(KERN_NOTICE "%s registered at %x\n", devinfo->name, devno); +} + + +int scull_access_init(dev_t firstdev) +{ + int result, i; + + /* Get our number space */ + result = register_chrdev_region (firstdev, SCULL_N_ADEVS, "sculla"); + if (result < 0) { + printk(KERN_WARNING "sculla: device number registration failed\n"); + return 0; + } + scull_a_firstdev = firstdev; + + /* Set up each device. */ + for (i = 0; i < SCULL_N_ADEVS; i++) + scull_access_setup (firstdev + i, scull_access_devs + i); + return SCULL_N_ADEVS; +} + +/* + * This is called by cleanup_module or on failure. + * It is required to never fail, even if nothing was initialized first + */ +void scull_access_cleanup(void) +{ + struct scull_listitem *lptr, *next; + int i; + + /* Clean up the static devs */ + for (i = 0; i < SCULL_N_ADEVS; i++) { + struct scull_dev *dev = scull_access_devs[i].sculldev; + cdev_del(&dev->cdev); + scull_trim(scull_access_devs[i].sculldev); + } + + /* And all the cloned devices */ + list_for_each_entry_safe(lptr, next, &scull_c_list, list) { + list_del(&lptr->list); + scull_trim(&(lptr->device)); + kfree(lptr); + } + + /* Free up our number space */ + unregister_chrdev_region(scull_a_firstdev, SCULL_N_ADEVS); + return; +} diff --git a/examples/scull/main.c b/examples/scull/main.c new file mode 100644 index 0000000..332f339 --- /dev/null +++ b/examples/scull/main.c @@ -0,0 +1,678 @@ +/* + * main.c -- the bare scull char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + */ + +//#include +#include +#include +#include + +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include + +//#include /* cli(), *_flags */ +#include +//#include /* copy_*_user */ +#include + +#include "scull.h" /* local definitions */ + +/* + * Our parameters which can be set at load time. + */ + +int scull_major = SCULL_MAJOR; +int scull_minor = 0; +int scull_nr_devs = SCULL_NR_DEVS; /* number of bare scull devices */ +int scull_quantum = SCULL_QUANTUM; +int scull_qset = SCULL_QSET; + +module_param(scull_major, int, S_IRUGO); +module_param(scull_minor, int, S_IRUGO); +module_param(scull_nr_devs, int, S_IRUGO); +module_param(scull_quantum, int, S_IRUGO); +module_param(scull_qset, int, S_IRUGO); + +MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct scull_dev *scull_devices; /* allocated in scull_init_module */ + + +/* + * Empty out the scull device; must be called with the device + * semaphore held. + */ +int scull_trim(struct scull_dev *dev) +{ + struct scull_qset *next, *dptr; + int qset = dev->qset; /* "dev" is not-null */ + int i; + + for (dptr = dev->data; dptr; dptr = next) { /* all the list items */ + if (dptr->data) { + for (i = 0; i < qset; i++) + kfree(dptr->data[i]); + kfree(dptr->data); + dptr->data = NULL; + } + next = dptr->next; + kfree(dptr); + } + dev->size = 0; + dev->quantum = scull_quantum; + dev->qset = scull_qset; + dev->data = NULL; + return 0; +} +#ifdef SCULL_DEBUG /* use proc only if debugging */ +/* + * The proc filesystem: function to read and entry + */ + +int scull_read_procmem(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, j, len = 0; + int limit = count - 80; /* Don't print more than this */ + + for (i = 0; i < scull_nr_devs && len <= limit; i++) { + struct scull_dev *d = &scull_devices[i]; + struct scull_qset *qs = d->data; + if (down_interruptible(&d->sem)) + return -ERESTARTSYS; + len += sprintf(buf+len,"\nDevice %i: qset %i, q %i, sz %li\n", + i, d->qset, d->quantum, d->size); + for (; qs && len <= limit; qs = qs->next) { /* scan the list */ + len += sprintf(buf + len, " item at %p, qset at %p\n", + qs, qs->data); + if (qs->data && !qs->next) /* dump only the last item */ + for (j = 0; j < d->qset; j++) { + if (qs->data[j]) + len += sprintf(buf + len, + " % 4i: %8p\n", + j, qs->data[j]); + } + } + up(&scull_devices[i].sem); + } + *eof = 1; + return len; +} + + +/* + * For now, the seq_file implementation will exist in parallel. The + * older read_procmem function should maybe go away, though. + */ + +/* + * Here are our sequence iteration methods. Our "position" is + * simply the device number. + */ +static void *scull_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= scull_nr_devs) + return NULL; /* No more to read */ + return scull_devices + *pos; +} + +static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + if (*pos >= scull_nr_devs) + return NULL; + return scull_devices + *pos; +} + +static void scull_seq_stop(struct seq_file *s, void *v) +{ + /* Actually, there's nothing to do here */ +} + +static int scull_seq_show(struct seq_file *s, void *v) +{ + struct scull_dev *dev = (struct scull_dev *) v; + struct scull_qset *d; + int i; + + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n", + (int) (dev - scull_devices), dev->qset, + dev->quantum, dev->size); + for (d = dev->data; d; d = d->next) { /* scan the list */ + seq_printf(s, " item at %p, qset at %p\n", d, d->data); + if (d->data && !d->next) /* dump only the last item */ + for (i = 0; i < dev->qset; i++) { + if (d->data[i]) + seq_printf(s, " % 4i: %8p\n", + i, d->data[i]); + } + } + up(&dev->sem); + return 0; +} + +/* + * Tie the sequence operators up. + */ +static struct seq_operations scull_seq_ops = { + .start = scull_seq_start, + .next = scull_seq_next, + .stop = scull_seq_stop, + .show = scull_seq_show +}; + +/* + * Now to implement the /proc file we need only make an open + * method which sets up the sequence operators. + */ +static int scull_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &scull_seq_ops); +} + +/* + * Create a set of file operations for our proc file. + */ +static struct file_operations scull_proc_ops = { + .owner = THIS_MODULE, + .open = scull_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + + +/* + * Actually create (and remove) the /proc file(s). + */ + +static void scull_create_proc(void) +{ + struct proc_dir_entry *entry; + //create_proc_read_entry("scullmem", 0 /* default mode */, + //NULL /* parent dir */, scull_read_procmem, + //NULL /* client data */); + + //entry = create_proc_entry("scullseq", 0, NULL); + entry = proc_create("scullseq", 0, NULL,&scull_proc_ops); + //if (entry) + //entry->proc_fops = &scull_proc_ops; +} + +static void scull_remove_proc(void) +{ + /* no problem if it was not registered */ + //remove_proc_entry("scullmem", NULL /* parent dir */); + remove_proc_entry("scullseq", NULL); +} + + +#endif /* SCULL_DEBUG */ + + + + + +/* + * Open and close + */ + +int scull_open(struct inode *inode, struct file *filp) +{ + struct scull_dev *dev; /* device information */ + + dev = container_of(inode->i_cdev, struct scull_dev, cdev); + filp->private_data = dev; /* for other methods */ + + /* now trim to 0 the length of the device if open was write-only */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + scull_trim(dev); /* ignore errors */ + up(&dev->sem); + } + return 0; /* success */ +} + +int scull_release(struct inode *inode, struct file *filp) +{ + return 0; +} +/* + * Follow the list + */ +struct scull_qset *scull_follow(struct scull_dev *dev, int n) +{ + struct scull_qset *qs = dev->data; + + /* Allocate first qset explicitly if need be */ + if (! qs) { + qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); + if (qs == NULL) + return NULL; /* Never mind */ + memset(qs, 0, sizeof(struct scull_qset)); + } + + /* Then follow the list */ + while (n--) { + if (!qs->next) { + qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL); + if (qs->next == NULL) + return NULL; /* Never mind */ + memset(qs->next, 0, sizeof(struct scull_qset)); + } + qs = qs->next; + continue; + } + return qs; +} + +/* + * Data management: read and write + */ + +ssize_t scull_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scull_dev *dev = filp->private_data; + struct scull_qset *dptr; /* the first listitem */ + int quantum = dev->quantum, qset = dev->qset; + int itemsize = quantum * qset; /* how many bytes in the listitem */ + int item, s_pos, q_pos, rest; + ssize_t retval = 0; + + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + if (*f_pos >= dev->size) + goto out; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + + /* find listitem, qset index, and offset in the quantum */ + item = (long)*f_pos / itemsize; + rest = (long)*f_pos % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position (defined elsewhere) */ + dptr = scull_follow(dev, item); + + if (dptr == NULL || !dptr->data || ! dptr->data[s_pos]) + goto out; /* don't fill holes */ + + /* read only up to the end of this quantum */ + if (count > quantum - q_pos) + count = quantum - q_pos; + + if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) { + retval = -EFAULT; + goto out; + } + *f_pos += count; + retval = count; + + out: + up(&dev->sem); + return retval; +} + +ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scull_dev *dev = filp->private_data; + struct scull_qset *dptr; + int quantum = dev->quantum, qset = dev->qset; + int itemsize = quantum * qset; + int item, s_pos, q_pos, rest; + ssize_t retval = -ENOMEM; /* value used in "goto out" statements */ + + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + + /* find listitem, qset index and offset in the quantum */ + item = (long)*f_pos / itemsize; + rest = (long)*f_pos % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ + dptr = scull_follow(dev, item); + if (dptr == NULL) + goto out; + if (!dptr->data) { + dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL); + if (!dptr->data) + goto out; + memset(dptr->data, 0, qset * sizeof(char *)); + } + if (!dptr->data[s_pos]) { + dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); + if (!dptr->data[s_pos]) + goto out; + } + /* write only up to the end of this quantum */ + if (count > quantum - q_pos) + count = quantum - q_pos; + + if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) { + retval = -EFAULT; + goto out; + } + *f_pos += count; + retval = count; + + /* update the size */ + if (dev->size < *f_pos) + dev->size = *f_pos; + + out: + up(&dev->sem); + return retval; +} + +/* + * The ioctl() implementation + */ + +long scull_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int err = 0, tmp; + int retval = 0; + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W + * transfers. `Type' is user-oriented, while + * access_ok is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) return -EFAULT; + + switch(cmd) { + + case SCULL_IOCRESET: + scull_quantum = SCULL_QUANTUM; + scull_qset = SCULL_QSET; + break; + + case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + retval = __get_user(scull_quantum, (int __user *)arg); + break; + + case SCULL_IOCTQUANTUM: /* Tell: arg is the value */ + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + scull_quantum = arg; + break; + + case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */ + retval = __put_user(scull_quantum, (int __user *)arg); + break; + + case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */ + return scull_quantum; + + case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */ + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + tmp = scull_quantum; + retval = __get_user(scull_quantum, (int __user *)arg); + if (retval == 0) + retval = __put_user(tmp, (int __user *)arg); + break; + + case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */ + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + tmp = scull_quantum; + scull_quantum = arg; + return tmp; + + case SCULL_IOCSQSET: + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + retval = __get_user(scull_qset, (int __user *)arg); + break; + + case SCULL_IOCTQSET: + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + scull_qset = arg; + break; + + case SCULL_IOCGQSET: + retval = __put_user(scull_qset, (int __user *)arg); + break; + + case SCULL_IOCQQSET: + return scull_qset; + + case SCULL_IOCXQSET: + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + tmp = scull_qset; + retval = __get_user(scull_qset, (int __user *)arg); + if (retval == 0) + retval = put_user(tmp, (int __user *)arg); + break; + + case SCULL_IOCHQSET: + if (! capable (CAP_SYS_ADMIN)) + return -EPERM; + tmp = scull_qset; + scull_qset = arg; + return tmp; + + /* + * The following two change the buffer size for scullpipe. + * The scullpipe device uses this same ioctl method, just to + * write less code. Actually, it's the same driver, isn't it? + */ + + case SCULL_P_IOCTSIZE: + scull_p_buffer = arg; + break; + + case SCULL_P_IOCQSIZE: + return scull_p_buffer; + + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + return retval; + +} + + + +/* + * The "extended" operations -- only seek + */ + +loff_t scull_llseek(struct file *filp, loff_t off, int whence) +{ + struct scull_dev *dev = filp->private_data; + loff_t newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happen */ + return -EINVAL; + } + if (newpos < 0) return -EINVAL; + filp->f_pos = newpos; + return newpos; +} + + + +struct file_operations scull_fops = { + .owner = THIS_MODULE, + .llseek = scull_llseek, + .read = scull_read, + .write = scull_write, + .unlocked_ioctl = scull_ioctl, + .open = scull_open, + .release = scull_release, +}; + +/* + * Finally, the module stuff + */ + +/* + * The cleanup function is used to handle initialization failures as well. + * Thefore, it must be careful to work correctly even if some of the items + * have not been initialized + */ +void scull_cleanup_module(void) +{ + int i; + dev_t devno = MKDEV(scull_major, scull_minor); + + /* Get rid of our char dev entries */ + if (scull_devices) { + for (i = 0; i < scull_nr_devs; i++) { + scull_trim(scull_devices + i); + cdev_del(&scull_devices[i].cdev); + } + kfree(scull_devices); + } + +#ifdef SCULL_DEBUG /* use proc only if debugging */ + scull_remove_proc(); +#endif + + /* cleanup_module is never called if registering failed */ + unregister_chrdev_region(devno, scull_nr_devs); + + /* and call the cleanup functions for friend devices */ + scull_p_cleanup(); + scull_access_cleanup(); + +} + + +/* + * Set up the char_dev structure for this device. + */ +static void scull_setup_cdev(struct scull_dev *dev, int index) +{ + int err, devno = MKDEV(scull_major, scull_minor + index); + + cdev_init(&dev->cdev, &scull_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &scull_fops; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scull%d", err, index); +} + + +int scull_init_module(void) +{ + int result, i; + dev_t dev = 0; + +/* + * Get a range of minor numbers to work with, asking for a dynamic + * major unless directed otherwise at load time. + */ + if (scull_major) { + dev = MKDEV(scull_major, scull_minor); + result = register_chrdev_region(dev, scull_nr_devs, "scull"); + } else { + result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, + "scull"); + scull_major = MAJOR(dev); + } + if (result < 0) { + printk(KERN_WARNING "scull: can't get major %d\n", scull_major); + return result; + } + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL); + if (!scull_devices) { + result = -ENOMEM; + goto fail; /* Make this more graceful */ + } + memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev)); + + /* Initialize each device. */ + for (i = 0; i < scull_nr_devs; i++) { + scull_devices[i].quantum = scull_quantum; + scull_devices[i].qset = scull_qset; + //init_MUTEX(&scull_devices[i].sem); + sema_init(&scull_devices[i].sem,1); + scull_setup_cdev(&scull_devices[i], i); + } + + /* At this point call the init function for any friend device */ + dev = MKDEV(scull_major, scull_minor + scull_nr_devs); + dev += scull_p_init(dev); + dev += scull_access_init(dev); + +#ifdef SCULL_DEBUG /* only when debugging */ + scull_create_proc(); +#endif + + return 0; /* succeed */ + + fail: + scull_cleanup_module(); + return result; +} + +module_init(scull_init_module); +module_exit(scull_cleanup_module); diff --git a/examples/scull/modules.order b/examples/scull/modules.order new file mode 100644 index 0000000..d5bbf4e --- /dev/null +++ b/examples/scull/modules.order @@ -0,0 +1 @@ +kernel//mnt/data/code/learn_linux_drive/examples/scull/scull.ko diff --git a/examples/scull/pipe.c b/examples/scull/pipe.c new file mode 100644 index 0000000..215adbe --- /dev/null +++ b/examples/scull/pipe.c @@ -0,0 +1,402 @@ +/* + * pipe.c -- fifo driver for scull + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + */ + +#include +#include + +#include /* printk(), min() */ +#include /* kmalloc() */ +#include /* everything... */ +#include +#include /* error codes */ +#include /* size_t */ +#include +#include +#include +#include +#include +#if 0 +#include +#endif + + +#include "scull.h" /* local definitions */ + +struct scull_pipe { + wait_queue_head_t inq, outq; /* read and write queues */ + char *buffer, *end; /* begin of buf, end of buf */ + int buffersize; /* used in pointer arithmetic */ + char *rp, *wp; /* where to read, where to write */ + int nreaders, nwriters; /* number of openings for r/w */ + struct fasync_struct *async_queue; /* asynchronous readers */ + struct semaphore sem; /* mutual exclusion semaphore */ + struct cdev cdev; /* Char device structure */ +}; + +/* parameters */ +static int scull_p_nr_devs = SCULL_P_NR_DEVS; /* number of pipe devices */ +int scull_p_buffer = SCULL_P_BUFFER; /* buffer size */ +dev_t scull_p_devno; /* Our first device number */ + +module_param(scull_p_nr_devs, int, 0); /* FIXME check perms */ +module_param(scull_p_buffer, int, 0); + +static struct scull_pipe *scull_p_devices; + +static int scull_p_fasync(int fd, struct file *filp, int mode); +static int spacefree(struct scull_pipe *dev); +/* + * Open and close + */ + + +static int scull_p_open(struct inode *inode, struct file *filp) +{ + struct scull_pipe *dev; + + dev = container_of(inode->i_cdev, struct scull_pipe, cdev); + filp->private_data = dev; + + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + if (!dev->buffer) { + /* allocate the buffer */ + dev->buffer = kmalloc(scull_p_buffer, GFP_KERNEL); + if (!dev->buffer) { + up(&dev->sem); + return -ENOMEM; + } + } + dev->buffersize = scull_p_buffer; + dev->end = dev->buffer + dev->buffersize; + dev->rp = dev->wp = dev->buffer; /* rd and wr from the beginning */ + + /* use f_mode,not f_flags: it's cleaner (fs/open.c tells why) */ + if (filp->f_mode & FMODE_READ) + dev->nreaders++; + if (filp->f_mode & FMODE_WRITE) + dev->nwriters++; + up(&dev->sem); + + return nonseekable_open(inode, filp); +} + + + +static int scull_p_release(struct inode *inode, struct file *filp) +{ + struct scull_pipe *dev = filp->private_data; + + /* remove this filp from the asynchronously notified filp's */ + scull_p_fasync(-1, filp, 0); + down(&dev->sem); + if (filp->f_mode & FMODE_READ) + dev->nreaders--; + if (filp->f_mode & FMODE_WRITE) + dev->nwriters--; + if (dev->nreaders + dev->nwriters == 0) { + kfree(dev->buffer); + dev->buffer = NULL; /* the other fields are not checked on open */ + } + up(&dev->sem); + return 0; +} + + +/* + * Data management: read and write + */ + +static ssize_t scull_p_read (struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scull_pipe *dev = filp->private_data; + + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + + while (dev->rp == dev->wp) { /* nothing to read */ + up(&dev->sem); /* release the lock */ + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + PDEBUG("\"%s\" reading: going to sleep\n", current->comm); + if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) + return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ + /* otherwise loop, but first reacquire the lock */ + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + } + /* ok, data is there, return something */ + if (dev->wp > dev->rp) + count = min(count, (size_t)(dev->wp - dev->rp)); + else /* the write pointer has wrapped, return data up to dev->end */ + count = min(count, (size_t)(dev->end - dev->rp)); + if (copy_to_user(buf, dev->rp, count)) { + up (&dev->sem); + return -EFAULT; + } + dev->rp += count; + if (dev->rp == dev->end) + dev->rp = dev->buffer; /* wrapped */ + up (&dev->sem); + + /* finally, awake any writers and return */ + wake_up_interruptible(&dev->outq); + PDEBUG("\"%s\" did read %li bytes\n",current->comm, (long)count); + return count; +} + +/* Wait for space for writing; caller must hold device semaphore. On + * error the semaphore will be released before returning. */ +static int scull_getwritespace(struct scull_pipe *dev, struct file *filp) +{ + while (spacefree(dev) == 0) { /* full */ + DEFINE_WAIT(wait); + + up(&dev->sem); + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + PDEBUG("\"%s\" writing: going to sleep\n",current->comm); + prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE); + if (spacefree(dev) == 0) + schedule(); + finish_wait(&dev->outq, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + } + return 0; +} + +/* How much space is free? */ +static int spacefree(struct scull_pipe *dev) +{ + if (dev->rp == dev->wp) + return dev->buffersize - 1; + return ((dev->rp + dev->buffersize - dev->wp) % dev->buffersize) - 1; +} + +static ssize_t scull_p_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scull_pipe *dev = filp->private_data; + int result; + + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + + /* Make sure there's space to write */ + result = scull_getwritespace(dev, filp); + if (result) + return result; /* scull_getwritespace called up(&dev->sem) */ + + /* ok, space is there, accept something */ + count = min(count, (size_t)spacefree(dev)); + if (dev->wp >= dev->rp) + count = min(count, (size_t)(dev->end - dev->wp)); /* to end-of-buf */ + else /* the write pointer has wrapped, fill up to rp-1 */ + count = min(count, (size_t)(dev->rp - dev->wp - 1)); + PDEBUG("Going to accept %li bytes to %p from %p\n", (long)count, dev->wp, buf); + if (copy_from_user(dev->wp, buf, count)) { + up (&dev->sem); + return -EFAULT; + } + dev->wp += count; + if (dev->wp == dev->end) + dev->wp = dev->buffer; /* wrapped */ + up(&dev->sem); + + /* finally, awake any reader */ + wake_up_interruptible(&dev->inq); /* blocked in read() and select() */ + + /* and signal asynchronous readers, explained late in chapter 5 */ + if (dev->async_queue) + kill_fasync(&dev->async_queue, SIGIO, POLL_IN); + PDEBUG("\"%s\" did write %li bytes\n",current->comm, (long)count); + return count; +} + +static unsigned int scull_p_poll(struct file *filp, poll_table *wait) +{ + struct scull_pipe *dev = filp->private_data; + unsigned int mask = 0; + + /* + * The buffer is circular; it is considered full + * if "wp" is right behind "rp" and empty if the + * two are equal. + */ + down(&dev->sem); + poll_wait(filp, &dev->inq, wait); + poll_wait(filp, &dev->outq, wait); + if (dev->rp != dev->wp) + mask |= POLLIN | POLLRDNORM; /* readable */ + if (spacefree(dev)) + mask |= POLLOUT | POLLWRNORM; /* writable */ + up(&dev->sem); + return mask; +} + + + + + +static int scull_p_fasync(int fd, struct file *filp, int mode) +{ + struct scull_pipe *dev = filp->private_data; + + return fasync_helper(fd, filp, mode, &dev->async_queue); +} + + + +/* FIXME this should use seq_file */ +#ifdef SCULL_DEBUG +static void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len) +{ + if (*offset == 0) + return; + if (*offset >= *len) { /* Not there yet */ + *offset -= *len; + *len = 0; + } + else { /* We're into the interesting stuff now */ + *start = buf + *offset; + *offset = 0; + } +} + + +static int scull_read_p_mem(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + int i, len; + struct scull_pipe *p; + +#define LIMIT (PAGE_SIZE-200) /* don't print any more after this size */ + *start = buf; + len = sprintf(buf, "Default buffersize is %i\n", scull_p_buffer); + for(i = 0; isem)) + return -ERESTARTSYS; + len += sprintf(buf+len, "\nDevice %i: %p\n", i, p); +/* len += sprintf(buf+len, " Queues: %p %p\n", p->inq, p->outq);*/ + len += sprintf(buf+len, " Buffer: %p to %p (%i bytes)\n", p->buffer, p->end, p->buffersize); + len += sprintf(buf+len, " rp %p wp %p\n", p->rp, p->wp); + len += sprintf(buf+len, " readers %i writers %i\n", p->nreaders, p->nwriters); + up(&p->sem); + scullp_proc_offset(buf, start, &offset, &len); + } + *eof = (len <= LIMIT); + return len; +} + + +#endif + + + +/* + * The file operations for the pipe device + * (some are overlayed with bare scull) + */ +struct file_operations scull_pipe_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = scull_p_read, + .write = scull_p_write, + .poll = scull_p_poll, + .unlocked_ioctl = scull_ioctl, + .open = scull_p_open, + .release = scull_p_release, + .fasync = scull_p_fasync, +}; + + +/* + * Set up a cdev entry. + */ +static void scull_p_setup_cdev(struct scull_pipe *dev, int index) +{ + int err, devno = scull_p_devno + index; + + cdev_init(&dev->cdev, &scull_pipe_fops); + dev->cdev.owner = THIS_MODULE; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scullpipe%d", err, index); +} + + + +/* + * Initialize the pipe devs; return how many we did. + */ +int scull_p_init(dev_t firstdev) +{ + int i, result; + + result = register_chrdev_region(firstdev, scull_p_nr_devs, "scullp"); + if (result < 0) { + printk(KERN_NOTICE "Unable to get scullp region, error %d\n", result); + return 0; + } + scull_p_devno = firstdev; + scull_p_devices = kmalloc(scull_p_nr_devs * sizeof(struct scull_pipe), GFP_KERNEL); + if (scull_p_devices == NULL) { + unregister_chrdev_region(firstdev, scull_p_nr_devs); + return 0; + } + memset(scull_p_devices, 0, scull_p_nr_devs * sizeof(struct scull_pipe)); + for (i = 0; i < scull_p_nr_devs; i++) { + init_waitqueue_head(&(scull_p_devices[i].inq)); + init_waitqueue_head(&(scull_p_devices[i].outq)); + //init_MUTEX(&scull_p_devices[i].sem); + sema_init(&scull_p_devices[i].sem,1); + scull_p_setup_cdev(scull_p_devices + i, i); + } +#ifdef SCULL_DEBUG + //create_proc_read_entry("scullpipe", 0, NULL, scull_read_p_mem, NULL); +#endif + return scull_p_nr_devs; +} + +/* + * This is called by cleanup_module or on failure. + * It is required to never fail, even if nothing was initialized first + */ +void scull_p_cleanup(void) +{ + int i; + +#ifdef SCULL_DEBUG + remove_proc_entry("scullpipe", NULL); +#endif + + if (!scull_p_devices) + return; /* nothing else to release */ + + for (i = 0; i < scull_p_nr_devs; i++) { + cdev_del(&scull_p_devices[i].cdev); + kfree(scull_p_devices[i].buffer); + } + kfree(scull_p_devices); + unregister_chrdev_region(scull_p_devno, scull_p_nr_devs); + scull_p_devices = NULL; /* pedantic */ +} diff --git a/examples/scull/scull.h b/examples/scull/scull.h new file mode 100644 index 0000000..f66470e --- /dev/null +++ b/examples/scull/scull.h @@ -0,0 +1,177 @@ +/* + * scull.h -- definitions for the char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $ + */ + +#ifndef _SCULL_H_ +#define _SCULL_H_ + +#include /* needed for the _IOW etc stuff used later */ + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SCULL_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + +#ifndef SCULL_MAJOR +#define SCULL_MAJOR 0 /* dynamic major by default */ +#endif + +#ifndef SCULL_NR_DEVS +#define SCULL_NR_DEVS 4 /* scull0 through scull3 */ +#endif + +#ifndef SCULL_P_NR_DEVS +#define SCULL_P_NR_DEVS 4 /* scullpipe0 through scullpipe3 */ +#endif + +/* + * The bare device is a variable-length region of memory. + * Use a linked list of indirect blocks. + * + * "scull_dev->data" points to an array of pointers, each + * pointer refers to a memory area of SCULL_QUANTUM bytes. + * + * The array (quantum-set) is SCULL_QSET long. + */ +#ifndef SCULL_QUANTUM +#define SCULL_QUANTUM 4000 +#endif + +#ifndef SCULL_QSET +#define SCULL_QSET 1000 +#endif + +/* + * The pipe device is a simple circular buffer. Here its default size + */ +#ifndef SCULL_P_BUFFER +#define SCULL_P_BUFFER 4000 +#endif + +/* + * Representation of scull quantum sets. + */ +struct scull_qset { + void **data; + struct scull_qset *next; +}; + +struct scull_dev { + struct scull_qset *data; /* Pointer to first quantum set */ + int quantum; /* the current quantum size */ + int qset; /* the current array size */ + unsigned long size; /* amount of data stored here */ + unsigned int access_key; /* used by sculluid and scullpriv */ + struct semaphore sem; /* mutual exclusion semaphore */ + struct cdev cdev; /* Char device structure */ +}; + +/* + * Split minors in two parts + */ +#define TYPE(minor) (((minor) >> 4) & 0xf) /* high nibble */ +#define NUM(minor) ((minor) & 0xf) /* low nibble */ + + +/* + * The different configurable parameters + */ +extern int scull_major; /* main.c */ +extern int scull_nr_devs; +extern int scull_quantum; +extern int scull_qset; + +extern int scull_p_buffer; /* pipe.c */ + + +/* + * Prototypes for shared functions + */ + +int scull_p_init(dev_t dev); +void scull_p_cleanup(void); +int scull_access_init(dev_t dev); +void scull_access_cleanup(void); + +int scull_trim(struct scull_dev *dev); + +ssize_t scull_read(struct file *filp, char __user *buf, size_t count, + loff_t *f_pos); +ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos); +loff_t scull_llseek(struct file *filp, loff_t off, int whence); +//int scull_ioctl(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg); +long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + + +/* + * Ioctl definitions + */ + +/* Use 'k' as magic number */ +#define SCULL_IOC_MAGIC 'k' +/* Please use a different 8-bit number in your code */ + +#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) + +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": switch G and S atomically + * H means "sHift": switch T and Q atomically + */ +#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) +#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) +#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) +#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) +#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) +#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) +#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) +#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) +#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) +#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int) +#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) +#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) + +/* + * The other entities only have "Tell" and "Query", because they're + * not printed in the book, and there's no need to have all six. + * (The previous stuff was only there to show different ways to do it. + */ +#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 13) +#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 14) +/* ... more to come */ + +#define SCULL_IOC_MAXNR 14 + +#endif /* _SCULL_H_ */ diff --git a/examples/scull/scull.init b/examples/scull/scull.init new file mode 100644 index 0000000..e0523ce --- /dev/null +++ b/examples/scull/scull.init @@ -0,0 +1,142 @@ +#!/bin/bash +# Sample init script for the a driver module + +DEVICE="scull" +SECTION="misc" + +# The list of filenames and minor numbers: $PREFIX is prefixed to all names +PREFIX="scull" +FILES=" 0 0 1 1 2 2 3 3 priv 16 + pipe0 32 pipe1 33 pipe2 34 pipe3 35 + single 48 uid 64 wuid 80" + +INSMOD=/sbin/insmod; # use /sbin/modprobe if you prefer + +function device_specific_post_load () { + true; # fill at will +} +function device_specific_pre_unload () { + true; # fill at will +} + +# Everything below this line should work unchanged for any char device. +# Obviously, however, no options on the command line: either in +# /etc/${DEVICE}.conf or /etc/modules.conf (if modprobe is used) + +# Optional configuration file: format is +# owner +# group +# mode +# options +CFG=/etc/${DEVICE}.conf + +# kernel version, used to look for modules +KERNEL=`uname -r` + +#FIXME: it looks like there is no misc section. Where should it be? +MODDIR="/lib/modules/${KERNEL}/kernel/drivers/${SECTION}" +if [ ! -d $MODDIR ]; then MODDIR="/lib/modules/${KERNEL}/${SECTION}"; fi + +# Root or die +if [ "$(id -u)" != "0" ] +then + echo "You must be root to load or unload kernel modules" + exit 1 +fi + +# Read configuration file +if [ -r $CFG ]; then + OWNER=`awk "\\$1==\"owner\" {print \\$2}" $CFG` + GROUP=`awk "\\$1==\"group\" {print \\$2}" $CFG` + MODE=`awk "\\$1==\"mode\" {print \\$2}" $CFG` + # The options string may include extra blanks or only blanks + OPTIONS=`sed -n '/^options / s/options //p' $CFG` +fi + + +# Create device files +function create_files () { + cd /dev + local devlist="" + local file + while true; do + if [ $# -lt 2 ]; then break; fi + file="${DEVICE}$1" + mknod $file c $MAJOR $2 + devlist="$devlist $file" + shift 2 + done + if [ -n "$OWNER" ]; then chown $OWNER $devlist; fi + if [ -n "$GROUP" ]; then chgrp $GROUP $devlist; fi + if [ -n "$MODE" ]; then chmod $MODE $devlist; fi +} + +# Remove device files +function remove_files () { + cd /dev + local devlist="" + local file + while true; do + if [ $# -lt 2 ]; then break; fi + file="${DEVICE}$1" + devlist="$devlist $file" + shift 2 + done + rm -f $devlist +} + +# Load and create files +function load_device () { + + if [ -f $MODDIR/$DEVICE.o ]; then + devpath=$MODDIR/$DEVICE.o + else if [ -f ./$DEVICE.o ]; then + devpath=./$DEVICE.o + else + devpath=$DEVICE; # let insmod/modprobe guess + fi; fi + if [ "$devpath" != "$DEVICE" ]; then + echo -n " (loading file $devpath)" + fi + + if $INSMOD $devpath $OPTIONS; then + MAJOR=`awk "\\$2==\"$DEVICE\" {print \\$1}" /proc/devices` + remove_files $FILES + create_files $FILES + device_specific_post_load + else + echo " FAILED!" + fi +} + +# Unload and remove files +function unload_device () { + device_specific_pre_unload + /sbin/rmmod $DEVICE + remove_files $FILES +} + + +case "$1" in + start) + echo -n "Loading $DEVICE" + load_device + echo "." + ;; + stop) + echo -n "Unloading $DEVICE" + unload_device + echo "." + ;; + force-reload|restart) + echo -n "Reloading $DEVICE" + unload_device + load_device + echo "." + ;; + *) + echo "Usage: $0 {start|stop|restart|force-reload}" + exit 1 +esac + +exit 0 diff --git a/examples/scull/scull_load b/examples/scull/scull_load new file mode 100644 index 0000000..0da1378 --- /dev/null +++ b/examples/scull/scull_load @@ -0,0 +1,66 @@ +#!/bin/sh +# $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $ +module="scull" +device="scull" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep -q '^staff:' /etc/group; then + group="staff" +else + group="wheel" +fi + +# invoke insmod with all arguments we got +# and use a pathname, as insmod doesn't look in . by default +/sbin/insmod ./$module.ko $* || exit 1 + +# retrieve major number +major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) + +# Remove stale nodes and replace them, then give gid and perms +# Usually the script is shorter, it's scull that has several devices in it. + +rm -f /dev/${device}[0-3] +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +ln -sf ${device}0 /dev/${device} +chgrp $group /dev/${device}[0-3] +chmod $mode /dev/${device}[0-3] + +rm -f /dev/${device}pipe[0-3] +mknod /dev/${device}pipe0 c $major 4 +mknod /dev/${device}pipe1 c $major 5 +mknod /dev/${device}pipe2 c $major 6 +mknod /dev/${device}pipe3 c $major 7 +ln -sf ${device}pipe0 /dev/${device}pipe +chgrp $group /dev/${device}pipe[0-3] +chmod $mode /dev/${device}pipe[0-3] + +rm -f /dev/${device}single +mknod /dev/${device}single c $major 8 +chgrp $group /dev/${device}single +chmod $mode /dev/${device}single + +rm -f /dev/${device}uid +mknod /dev/${device}uid c $major 9 +chgrp $group /dev/${device}uid +chmod $mode /dev/${device}uid + +rm -f /dev/${device}wuid +mknod /dev/${device}wuid c $major 10 +chgrp $group /dev/${device}wuid +chmod $mode /dev/${device}wuid + +rm -f /dev/${device}priv +mknod /dev/${device}priv c $major 11 +chgrp $group /dev/${device}priv +chmod $mode /dev/${device}priv + + + + + + diff --git a/examples/scull/scull_unload b/examples/scull/scull_unload new file mode 100644 index 0000000..269a0aa --- /dev/null +++ b/examples/scull/scull_unload @@ -0,0 +1,20 @@ +#!/bin/sh +module="scull" +device="scull" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes + +rm -f /dev/${device} /dev/${device}[0-3] +rm -f /dev/${device}priv +rm -f /dev/${device}pipe /dev/${device}pipe[0-3] +rm -f /dev/${device}single +rm -f /dev/${device}uid +rm -f /dev/${device}wuid + + + + + diff --git a/examples/scullc/Makefile b/examples/scullc/Makefile new file mode 100644 index 0000000..9b90b41 --- /dev/null +++ b/examples/scullc/Makefile @@ -0,0 +1,46 @@ + +# Comment/uncomment the following line to enable/disable debugging +#DEBUG = y + + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULLC_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) -I$(LDDINC) + +TARGET = scullc + +ifneq ($(KERNELRELEASE),) + +scullc-objs := main.o + +obj-m := scullc.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD) modules + +endif + + +install: + install -d $(INSTALLDIR) + install -c $(TARGET).o $(INSTALLDIR) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/scullc/main.c b/examples/scullc/main.c new file mode 100644 index 0000000..f7a1ca5 --- /dev/null +++ b/examples/scullc/main.c @@ -0,0 +1,600 @@ +/* -*- C -*- + * main.c -- the bare scullc char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $ + */ + +#include +#include +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include "scullc.h" /* local definitions */ + + +int scullc_major = SCULLC_MAJOR; +int scullc_devs = SCULLC_DEVS; /* number of bare scullc devices */ +int scullc_qset = SCULLC_QSET; +int scullc_quantum = SCULLC_QUANTUM; + +module_param(scullc_major, int, 0); +module_param(scullc_devs, int, 0); +module_param(scullc_qset, int, 0); +module_param(scullc_quantum, int, 0); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct scullc_dev *scullc_devices; /* allocated in scullc_init */ + +int scullc_trim(struct scullc_dev *dev); +void scullc_cleanup(void); + +/* declare one cache pointer: use it for all devices */ +kmem_cache_t *scullc_cache; + + + + + +#ifdef SCULLC_USE_PROC /* don't waste space if unused */ +/* + * The proc filesystem: function to read and entry + */ + +void scullc_proc_offset(char *buf, char **start, off_t *offset, int *len) +{ + if (*offset == 0) + return; + if (*offset >= *len) { + /* Not there yet */ + *offset -= *len; + *len = 0; + } else { + /* We're into the interesting stuff now */ + *start = buf + *offset; + *offset = 0; + } +} + +/* FIXME: Do we need this here?? It be ugly */ +int scullc_read_procmem(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, j, quantum, qset, len = 0; + int limit = count - 80; /* Don't print more than this */ + struct scullc_dev *d; + + *start = buf; + for(i = 0; i < scullc_devs; i++) { + d = &scullc_devices[i]; + if (down_interruptible (&d->sem)) + return -ERESTARTSYS; + qset = d->qset; /* retrieve the features of each device */ + quantum=d->quantum; + len += sprintf(buf+len,"\nDevice %i: qset %i, quantum %i, sz %li\n", + i, qset, quantum, (long)(d->size)); + for (; d; d = d->next) { /* scan the list */ + len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data); + scullc_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + if (d->data && !d->next) /* dump only the last item - save space */ + for (j = 0; j < qset; j++) { + if (d->data[j]) + len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]); + scullc_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + } + } + out: + up (&scullc_devices[i].sem); + if (len > limit) + break; + } + *eof = 1; + return len; +} + +#endif /* SCULLC_USE_PROC */ + +/* + * Open and close + */ + +int scullc_open (struct inode *inode, struct file *filp) +{ + struct scullc_dev *dev; /* device information */ + + /* Find the device */ + dev = container_of(inode->i_cdev, struct scullc_dev, cdev); + + /* now trim to 0 the length of the device if open was write-only */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + scullc_trim(dev); /* ignore errors */ + up (&dev->sem); + } + + /* and use filp->private_data to point to the device data */ + filp->private_data = dev; + + return 0; /* success */ +} + +int scullc_release (struct inode *inode, struct file *filp) +{ + return 0; +} + +/* + * Follow the list + */ +struct scullc_dev *scullc_follow(struct scullc_dev *dev, int n) +{ + while (n--) { + if (!dev->next) { + dev->next = kmalloc(sizeof(struct scullc_dev), GFP_KERNEL); + memset(dev->next, 0, sizeof(struct scullc_dev)); + } + dev = dev->next; + continue; + } + return dev; +} + +/* + * Data management: read and write + */ + +ssize_t scullc_read (struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullc_dev *dev = filp->private_data; /* the first listitem */ + struct scullc_dev *dptr; + int quantum = dev->quantum; + int qset = dev->qset; + int itemsize = quantum * qset; /* how many bytes in the listitem */ + int item, s_pos, q_pos, rest; + ssize_t retval = 0; + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + if (*f_pos > dev->size) + goto nothing; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + /* find listitem, qset index, and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position (defined elsewhere) */ + dptr = scullc_follow(dev, item); + + if (!dptr->data) + goto nothing; /* don't fill holes */ + if (!dptr->data[s_pos]) + goto nothing; + if (count > quantum - q_pos) + count = quantum - q_pos; /* read only up to the end of this quantum */ + + if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) { + retval = -EFAULT; + goto nothing; + } + up (&dev->sem); + + *f_pos += count; + return count; + + nothing: + up (&dev->sem); + return retval; +} + + + +ssize_t scullc_write (struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullc_dev *dev = filp->private_data; + struct scullc_dev *dptr; + int quantum = dev->quantum; + int qset = dev->qset; + int itemsize = quantum * qset; + int item, s_pos, q_pos, rest; + ssize_t retval = -ENOMEM; /* our most likely error */ + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + + /* find listitem, qset index and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ + dptr = scullc_follow(dev, item); + if (!dptr->data) { + dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL); + if (!dptr->data) + goto nomem; + memset(dptr->data, 0, qset * sizeof(char *)); + } + /* Allocate a quantum using the memory cache */ + if (!dptr->data[s_pos]) { + dptr->data[s_pos] = kmem_cache_alloc(scullc_cache, GFP_KERNEL); + if (!dptr->data[s_pos]) + goto nomem; + memset(dptr->data[s_pos], 0, scullc_quantum); + } + if (count > quantum - q_pos) + count = quantum - q_pos; /* write only up to the end of this quantum */ + if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) { + retval = -EFAULT; + goto nomem; + } + *f_pos += count; + + /* update the size */ + if (dev->size < *f_pos) + dev->size = *f_pos; + up (&dev->sem); + return count; + + nomem: + up (&dev->sem); + return retval; +} + +/* + * The ioctl() implementation + */ + +int scullc_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int err = 0, ret = 0, tmp; + + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (_IOC_TYPE(cmd) != SCULLC_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SCULLC_IOC_MAXNR) return -ENOTTY; + + /* + * the type is a bitmask, and VERIFY_WRITE catches R/W + * transfers. Note that the type is user-oriented, while + * verify_area is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch(cmd) { + + case SCULLC_IOCRESET: + scullc_qset = SCULLC_QSET; + scullc_quantum = SCULLC_QUANTUM; + break; + + case SCULLC_IOCSQUANTUM: /* Set: arg points to the value */ + ret = __get_user(scullc_quantum, (int __user *) arg); + break; + + case SCULLC_IOCTQUANTUM: /* Tell: arg is the value */ + scullc_quantum = arg; + break; + + case SCULLC_IOCGQUANTUM: /* Get: arg is pointer to result */ + ret = __put_user (scullc_quantum, (int __user *) arg); + break; + + case SCULLC_IOCQQUANTUM: /* Query: return it (it's positive) */ + return scullc_quantum; + + case SCULLC_IOCXQUANTUM: /* eXchange: use arg as pointer */ + tmp = scullc_quantum; + ret = __get_user(scullc_quantum, (int __user *) arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *) arg); + break; + + case SCULLC_IOCHQUANTUM: /* sHift: like Tell + Query */ + tmp = scullc_quantum; + scullc_quantum = arg; + return tmp; + + case SCULLC_IOCSQSET: + ret = __get_user(scullc_qset, (int __user *) arg); + break; + + case SCULLC_IOCTQSET: + scullc_qset = arg; + break; + + case SCULLC_IOCGQSET: + ret = __put_user(scullc_qset, (int __user *)arg); + break; + + case SCULLC_IOCQQSET: + return scullc_qset; + + case SCULLC_IOCXQSET: + tmp = scullc_qset; + ret = __get_user(scullc_qset, (int __user *)arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *)arg); + break; + + case SCULLC_IOCHQSET: + tmp = scullc_qset; + scullc_qset = arg; + return tmp; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return ret; +} + +/* + * The "extended" operations + */ + +loff_t scullc_llseek (struct file *filp, loff_t off, int whence) +{ + struct scullc_dev *dev = filp->private_data; + long newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happen */ + return -EINVAL; + } + if (newpos<0) return -EINVAL; + filp->f_pos = newpos; + return newpos; +} + + +/* + * A simple asynchronous I/O implementation. + */ + +struct async_work { + struct kiocb *iocb; + int result; + struct work_struct work; +}; + +/* + * "Complete" an asynchronous operation. + */ +static void scullc_do_deferred_op(void *p) +{ + struct async_work *stuff = (struct async_work *) p; + aio_complete(stuff->iocb, stuff->result, 0); + kfree(stuff); +} + + +static int scullc_defer_op(int write, struct kiocb *iocb, char __user *buf, + size_t count, loff_t pos) +{ + struct async_work *stuff; + int result; + + /* Copy now while we can access the buffer */ + if (write) + result = scullc_write(iocb->ki_filp, buf, count, &pos); + else + result = scullc_read(iocb->ki_filp, buf, count, &pos); + + /* If this is a synchronous IOCB, we return our status now. */ + if (is_sync_kiocb(iocb)) + return result; + + /* Otherwise defer the completion for a few milliseconds. */ + stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); + if (stuff == NULL) + return result; /* No memory, just complete now */ + stuff->iocb = iocb; + stuff->result = result; + INIT_WORK(&stuff->work, scullc_do_deferred_op, stuff); + schedule_delayed_work(&stuff->work, HZ/100); + return -EIOCBQUEUED; +} + + +static ssize_t scullc_aio_read(struct kiocb *iocb, char __user *buf, size_t count, + loff_t pos) +{ + return scullc_defer_op(0, iocb, buf, count, pos); +} + +static ssize_t scullc_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos) +{ + return scullc_defer_op(1, iocb, (char __user *) buf, count, pos); +} + + + + +/* + * The fops + */ + +struct file_operations scullc_fops = { + .owner = THIS_MODULE, + .llseek = scullc_llseek, + .read = scullc_read, + .write = scullc_write, + .ioctl = scullc_ioctl, + .open = scullc_open, + .release = scullc_release, + .aio_read = scullc_aio_read, + .aio_write = scullc_aio_write, +}; + +int scullc_trim(struct scullc_dev *dev) +{ + struct scullc_dev *next, *dptr; + int qset = dev->qset; /* "dev" is not-null */ + int i; + + if (dev->vmas) /* don't trim: there are active mappings */ + return -EBUSY; + + for (dptr = dev; dptr; dptr = next) { /* all the list items */ + if (dptr->data) { + for (i = 0; i < qset; i++) + if (dptr->data[i]) + kmem_cache_free(scullc_cache, dptr->data[i]); + + kfree(dptr->data); + dptr->data=NULL; + } + next=dptr->next; + if (dptr != dev) kfree(dptr); /* all of them but the first */ + } + dev->size = 0; + dev->qset = scullc_qset; + dev->quantum = scullc_quantum; + dev->next = NULL; + return 0; +} + + +static void scullc_setup_cdev(struct scullc_dev *dev, int index) +{ + int err, devno = MKDEV(scullc_major, index); + + cdev_init(&dev->cdev, &scullc_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &scullc_fops; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scull%d", err, index); +} + + + +/* + * Finally, the module stuff + */ + +int scullc_init(void) +{ + int result, i; + dev_t dev = MKDEV(scullc_major, 0); + + /* + * Register your major, and accept a dynamic number. + */ + if (scullc_major) + result = register_chrdev_region(dev, scullc_devs, "scullc"); + else { + result = alloc_chrdev_region(&dev, 0, scullc_devs, "scullc"); + scullc_major = MAJOR(dev); + } + if (result < 0) + return result; + + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + scullc_devices = kmalloc(scullc_devs*sizeof (struct scullc_dev), GFP_KERNEL); + if (!scullc_devices) { + result = -ENOMEM; + goto fail_malloc; + } + memset(scullc_devices, 0, scullc_devs*sizeof (struct scullc_dev)); + for (i = 0; i < scullc_devs; i++) { + scullc_devices[i].quantum = scullc_quantum; + scullc_devices[i].qset = scullc_qset; + sema_init (&scullc_devices[i].sem, 1); + scullc_setup_cdev(scullc_devices + i, i); + } + + scullc_cache = kmem_cache_create("scullc", scullc_quantum, + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); /* no ctor/dtor */ + if (!scullc_cache) { + scullc_cleanup(); + return -ENOMEM; + } + +#ifdef SCULLC_USE_PROC /* only when available */ + create_proc_read_entry("scullcmem", 0, NULL, scullc_read_procmem, NULL); +#endif + return 0; /* succeed */ + + fail_malloc: + unregister_chrdev_region(dev, scullc_devs); + return result; +} + + + +void scullc_cleanup(void) +{ + int i; + +#ifdef SCULLC_USE_PROC + remove_proc_entry("scullcmem", NULL); +#endif + + for (i = 0; i < scullc_devs; i++) { + cdev_del(&scullc_devices[i].cdev); + scullc_trim(scullc_devices + i); + } + kfree(scullc_devices); + + if (scullc_cache) + kmem_cache_destroy(scullc_cache); + unregister_chrdev_region(MKDEV (scullc_major, 0), scullc_devs); +} + + +module_init(scullc_init); +module_exit(scullc_cleanup); diff --git a/examples/scullc/mmap.c b/examples/scullc/mmap.c new file mode 100644 index 0000000..841f62c --- /dev/null +++ b/examples/scullc/mmap.c @@ -0,0 +1,118 @@ +/* -*- C -*- + * mmap.c -- memory mapping for the scullc char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ + */ + +#include +#include + +#include /* everything */ +#include /* error codes */ +#include + +#include "scullc.h" /* local definitions */ + + +/* + * open and close: just keep track of how many times the device is + * mapped, to avoid releasing it. + */ + +void scullc_vma_open(struct vm_area_struct *vma) +{ + struct scullc_dev *dev = vma->vm_private_data; + + dev->vmas++; +} + +void scullc_vma_close(struct vm_area_struct *vma) +{ + struct scullc_dev *dev = vma->vm_private_data; + + dev->vmas--; +} + +/* + * The nopage method: the core of the file. It retrieves the + * page required from the scullc device and returns it to the + * user. The count for the page must be incremented, because + * it is automatically decremented at page unmap. + * + * For this reason, "order" must be zero. Otherwise, only the first + * page has its count incremented, and the allocating module must + * release it as a whole block. Therefore, it isn't possible to map + * pages from a multipage block: when they are unmapped, their count + * is individually decreased, and would drop to 0. + */ + +struct page *scullc_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset; + struct scullc_dev *ptr, *dev = vma->vm_private_data; + struct page *page = NOPAGE_SIGBUS; + void *pageptr = NULL; /* default to "missing" */ + + down(&dev->sem); + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + if (offset >= dev->size) goto out; /* out of range */ + + /* + * Now retrieve the scullc device from the list,then the page. + * If the device has holes, the process receives a SIGBUS when + * accessing the hole. + */ + offset >>= PAGE_SHIFT; /* offset is a number of pages */ + for (ptr = dev; ptr && offset >= dev->qset;) { + ptr = ptr->next; + offset -= dev->qset; + } + if (ptr && ptr->data) pageptr = ptr->data[offset]; + if (!pageptr) goto out; /* hole or end-of-file */ + + /* got it, now increment the count */ + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + out: + up(&dev->sem); + return page; +} + + + +struct vm_operations_struct scullc_vm_ops = { + .open = scullc_vma_open, + .close = scullc_vma_close, + .nopage = scullc_vma_nopage, +}; + + +int scullc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct inode *inode = filp->f_dentry->d_inode; + + /* refuse to map if order is not 0 */ + if (scullc_devices[iminor(inode)].order) + return -ENODEV; + + /* don't do anything here: "nopage" will set up page table entries */ + vma->vm_ops = &scullc_vm_ops; + vma->vm_flags |= VM_RESERVED; + vma->vm_private_data = filp->private_data; + scullc_vma_open(vma); + return 0; +} + diff --git a/examples/scullc/scullc.h b/examples/scullc/scullc.h new file mode 100644 index 0000000..86d5090 --- /dev/null +++ b/examples/scullc/scullc.h @@ -0,0 +1,122 @@ +/* -*- C -*- + * scullc.h -- definitions for the scullc char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SCULLC_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullc: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + +#define SCULLC_MAJOR 0 /* dynamic major by default */ + +#define SCULLC_DEVS 4 /* scullc0 through scullc3 */ + +/* + * The bare device is a variable-length region of memory. + * Use a linked list of indirect blocks. + * + * "scullc_dev->data" points to an array of pointers, each + * pointer refers to a memory page. + * + * The array (quantum-set) is SCULLC_QSET long. + */ +#define SCULLC_QUANTUM 4000 /* use a quantum size like scull */ +#define SCULLC_QSET 500 + +struct scullc_dev { + void **data; + struct scullc_dev *next; /* next listitem */ + int vmas; /* active mappings */ + int quantum; /* the current allocation size */ + int qset; /* the current array size */ + size_t size; /* 32-bit will suffice */ + struct semaphore sem; /* Mutual exclusion */ + struct cdev cdev; +}; + +extern struct scullc_dev *scullc_devices; + +extern struct file_operations scullc_fops; + +/* + * The different configurable parameters + */ +extern int scullc_major; /* main.c */ +extern int scullc_devs; +extern int scullc_order; +extern int scullc_qset; + +/* + * Prototypes for shared functions + */ +int scullc_trim(struct scullc_dev *dev); +struct scullc_dev *scullc_follow(struct scullc_dev *dev, int n); + + +#ifdef SCULLC_DEBUG +# define SCULLC_USE_PROC +#endif + +/* + * Ioctl definitions + */ + +/* Use 'K' as magic number */ +#define SCULLC_IOC_MAGIC 'K' + +#define SCULLC_IOCRESET _IO(SCULLC_IOC_MAGIC, 0) + +/* + * S means "Set" through a ptr, + * T means "Tell" directly + * G means "Get" (to a pointed var) + * Q means "Query", response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define SCULLC_IOCSQUANTUM _IOW(SCULLC_IOC_MAGIC, 1, int) +#define SCULLC_IOCTQUANTUM _IO(SCULLC_IOC_MAGIC, 2) +#define SCULLC_IOCGQUANTUM _IOR(SCULLC_IOC_MAGIC, 3, int) +#define SCULLC_IOCQQUANTUM _IO(SCULLC_IOC_MAGIC, 4) +#define SCULLC_IOCXQUANTUM _IOWR(SCULLC_IOC_MAGIC, 5, int) +#define SCULLC_IOCHQUANTUM _IO(SCULLC_IOC_MAGIC, 6) +#define SCULLC_IOCSQSET _IOW(SCULLC_IOC_MAGIC, 7, int) +#define SCULLC_IOCTQSET _IO(SCULLC_IOC_MAGIC, 8) +#define SCULLC_IOCGQSET _IOR(SCULLC_IOC_MAGIC, 9, int) +#define SCULLC_IOCQQSET _IO(SCULLC_IOC_MAGIC, 10) +#define SCULLC_IOCXQSET _IOWR(SCULLC_IOC_MAGIC,11, int) +#define SCULLC_IOCHQSET _IO(SCULLC_IOC_MAGIC, 12) + +#define SCULLC_IOC_MAXNR 12 + + + diff --git a/examples/scullc/scullc_load b/examples/scullc/scullc_load new file mode 100644 index 0000000..8ca1f69 --- /dev/null +++ b/examples/scullc/scullc_load @@ -0,0 +1,30 @@ +#!/bin/sh +module="scullc" +device="scullc" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# remove stale nodes +rm -f /dev/${device}? + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +ln -sf ${device}0 /dev/${device} + +# give appropriate group/permissions +chgrp $group /dev/${device}[0-3] +chmod $mode /dev/${device}[0-3] diff --git a/examples/scullc/scullc_unload b/examples/scullc/scullc_unload new file mode 100644 index 0000000..bb91f71 --- /dev/null +++ b/examples/scullc/scullc_unload @@ -0,0 +1,11 @@ +#!/bin/sh +module="scullc" +device="scullc" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# remove nodes +rm -f /dev/${device}[0-3] /dev/${device} + +exit 0 diff --git a/examples/sculld/Makefile b/examples/sculld/Makefile new file mode 100644 index 0000000..29e9fc3 --- /dev/null +++ b/examples/sculld/Makefile @@ -0,0 +1,46 @@ + +# Comment/uncomment the following line to enable/disable debugging +#DEBUG = y + + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULLD_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) -I$(LDDINC) + +TARGET = sculld + +ifneq ($(KERNELRELEASE),) + +sculld-objs := main.o mmap.o + +obj-m := sculld.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD) modules + +endif + + +install: + install -d $(INSTALLDIR) + install -c $(TARGET).o $(INSTALLDIR) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/sculld/main.c b/examples/sculld/main.c new file mode 100644 index 0000000..a4854a5 --- /dev/null +++ b/examples/sculld/main.c @@ -0,0 +1,632 @@ +/* -*- C -*- + * main.c -- the bare sculld char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $ + */ + +#include +#include +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include "sculld.h" /* local definitions */ + + +int sculld_major = SCULLD_MAJOR; +int sculld_devs = SCULLD_DEVS; /* number of bare sculld devices */ +int sculld_qset = SCULLD_QSET; +int sculld_order = SCULLD_ORDER; + +module_param(sculld_major, int, 0); +module_param(sculld_devs, int, 0); +module_param(sculld_qset, int, 0); +module_param(sculld_order, int, 0); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct sculld_dev *sculld_devices; /* allocated in sculld_init */ + +int sculld_trim(struct sculld_dev *dev); +void sculld_cleanup(void); + + + +/* Device model stuff */ + +static struct ldd_driver sculld_driver = { + .version = "$Revision: 1.21 $", + .module = THIS_MODULE, + .driver = { + .name = "sculld", + }, +}; + + + +#ifdef SCULLD_USE_PROC /* don't waste space if unused */ +/* + * The proc filesystem: function to read and entry + */ + +void sculld_proc_offset(char *buf, char **start, off_t *offset, int *len) +{ + if (*offset == 0) + return; + if (*offset >= *len) { + /* Not there yet */ + *offset -= *len; + *len = 0; + } else { + /* We're into the interesting stuff now */ + *start = buf + *offset; + *offset = 0; + } +} + +/* FIXME: Do we need this here?? It be ugly */ +int sculld_read_procmem(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, j, order, qset, len = 0; + int limit = count - 80; /* Don't print more than this */ + struct sculld_dev *d; + + *start = buf; + for(i = 0; i < sculld_devs; i++) { + d = &sculld_devices[i]; + if (down_interruptible (&d->sem)) + return -ERESTARTSYS; + qset = d->qset; /* retrieve the features of each device */ + order = d->order; + len += sprintf(buf+len,"\nDevice %i: qset %i, order %i, sz %li\n", + i, qset, order, (long)(d->size)); + for (; d; d = d->next) { /* scan the list */ + len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data); + sculld_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + if (d->data && !d->next) /* dump only the last item - save space */ + for (j = 0; j < qset; j++) { + if (d->data[j]) + len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]); + sculld_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + } + } + out: + up (&sculld_devices[i].sem); + if (len > limit) + break; + } + *eof = 1; + return len; +} + +#endif /* SCULLD_USE_PROC */ + +/* + * Open and close + */ + +int sculld_open (struct inode *inode, struct file *filp) +{ + struct sculld_dev *dev; /* device information */ + + /* Find the device */ + dev = container_of(inode->i_cdev, struct sculld_dev, cdev); + + /* now trim to 0 the length of the device if open was write-only */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + sculld_trim(dev); /* ignore errors */ + up (&dev->sem); + } + + /* and use filp->private_data to point to the device data */ + filp->private_data = dev; + + return 0; /* success */ +} + +int sculld_release (struct inode *inode, struct file *filp) +{ + return 0; +} + +/* + * Follow the list + */ +struct sculld_dev *sculld_follow(struct sculld_dev *dev, int n) +{ + while (n--) { + if (!dev->next) { + dev->next = kmalloc(sizeof(struct sculld_dev), GFP_KERNEL); + memset(dev->next, 0, sizeof(struct sculld_dev)); + } + dev = dev->next; + continue; + } + return dev; +} + +/* + * Data management: read and write + */ + +ssize_t sculld_read (struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct sculld_dev *dev = filp->private_data; /* the first listitem */ + struct sculld_dev *dptr; + int quantum = PAGE_SIZE << dev->order; + int qset = dev->qset; + int itemsize = quantum * qset; /* how many bytes in the listitem */ + int item, s_pos, q_pos, rest; + ssize_t retval = 0; + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + if (*f_pos > dev->size) + goto nothing; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + /* find listitem, qset index, and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position (defined elsewhere) */ + dptr = sculld_follow(dev, item); + + if (!dptr->data) + goto nothing; /* don't fill holes */ + if (!dptr->data[s_pos]) + goto nothing; + if (count > quantum - q_pos) + count = quantum - q_pos; /* read only up to the end of this quantum */ + + if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) { + retval = -EFAULT; + goto nothing; + } + up (&dev->sem); + + *f_pos += count; + return count; + + nothing: + up (&dev->sem); + return retval; +} + + + +ssize_t sculld_write (struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct sculld_dev *dev = filp->private_data; + struct sculld_dev *dptr; + int quantum = PAGE_SIZE << dev->order; + int qset = dev->qset; + int itemsize = quantum * qset; + int item, s_pos, q_pos, rest; + ssize_t retval = -ENOMEM; /* our most likely error */ + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + + /* find listitem, qset index and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ + dptr = sculld_follow(dev, item); + if (!dptr->data) { + dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL); + if (!dptr->data) + goto nomem; + memset(dptr->data, 0, qset * sizeof(char *)); + } + /* Here's the allocation of a single quantum */ + if (!dptr->data[s_pos]) { + dptr->data[s_pos] = + (void *)__get_free_pages(GFP_KERNEL, dptr->order); + if (!dptr->data[s_pos]) + goto nomem; + memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order); + } + if (count > quantum - q_pos) + count = quantum - q_pos; /* write only up to the end of this quantum */ + if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) { + retval = -EFAULT; + goto nomem; + } + *f_pos += count; + + /* update the size */ + if (dev->size < *f_pos) + dev->size = *f_pos; + up (&dev->sem); + return count; + + nomem: + up (&dev->sem); + return retval; +} + +/* + * The ioctl() implementation + */ + +int sculld_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int err = 0, ret = 0, tmp; + + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (_IOC_TYPE(cmd) != SCULLD_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SCULLD_IOC_MAXNR) return -ENOTTY; + + /* + * the type is a bitmask, and VERIFY_WRITE catches R/W + * transfers. Note that the type is user-oriented, while + * verify_area is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch(cmd) { + + case SCULLD_IOCRESET: + sculld_qset = SCULLD_QSET; + sculld_order = SCULLD_ORDER; + break; + + case SCULLD_IOCSORDER: /* Set: arg points to the value */ + ret = __get_user(sculld_order, (int __user *) arg); + break; + + case SCULLD_IOCTORDER: /* Tell: arg is the value */ + sculld_order = arg; + break; + + case SCULLD_IOCGORDER: /* Get: arg is pointer to result */ + ret = __put_user (sculld_order, (int __user *) arg); + break; + + case SCULLD_IOCQORDER: /* Query: return it (it's positive) */ + return sculld_order; + + case SCULLD_IOCXORDER: /* eXchange: use arg as pointer */ + tmp = sculld_order; + ret = __get_user(sculld_order, (int __user *) arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *) arg); + break; + + case SCULLD_IOCHORDER: /* sHift: like Tell + Query */ + tmp = sculld_order; + sculld_order = arg; + return tmp; + + case SCULLD_IOCSQSET: + ret = __get_user(sculld_qset, (int __user *) arg); + break; + + case SCULLD_IOCTQSET: + sculld_qset = arg; + break; + + case SCULLD_IOCGQSET: + ret = __put_user(sculld_qset, (int __user *)arg); + break; + + case SCULLD_IOCQQSET: + return sculld_qset; + + case SCULLD_IOCXQSET: + tmp = sculld_qset; + ret = __get_user(sculld_qset, (int __user *)arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *)arg); + break; + + case SCULLD_IOCHQSET: + tmp = sculld_qset; + sculld_qset = arg; + return tmp; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return ret; +} + +/* + * The "extended" operations + */ + +loff_t sculld_llseek (struct file *filp, loff_t off, int whence) +{ + struct sculld_dev *dev = filp->private_data; + long newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happen */ + return -EINVAL; + } + if (newpos<0) return -EINVAL; + filp->f_pos = newpos; + return newpos; +} + + +/* + * A simple asynchronous I/O implementation. + */ + +struct async_work { + struct kiocb *iocb; + int result; + struct work_struct work; +}; + +/* + * "Complete" an asynchronous operation. + */ +static void sculld_do_deferred_op(void *p) +{ + struct async_work *stuff = (struct async_work *) p; + aio_complete(stuff->iocb, stuff->result, 0); + kfree(stuff); +} + + +static int sculld_defer_op(int write, struct kiocb *iocb, char __user *buf, + size_t count, loff_t pos) +{ + struct async_work *stuff; + int result; + + /* Copy now while we can access the buffer */ + if (write) + result = sculld_write(iocb->ki_filp, buf, count, &pos); + else + result = sculld_read(iocb->ki_filp, buf, count, &pos); + + /* If this is a synchronous IOCB, we return our status now. */ + if (is_sync_kiocb(iocb)) + return result; + + /* Otherwise defer the completion for a few milliseconds. */ + stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); + if (stuff == NULL) + return result; /* No memory, just complete now */ + stuff->iocb = iocb; + stuff->result = result; + INIT_WORK(&stuff->work, sculld_do_deferred_op, stuff); + schedule_delayed_work(&stuff->work, HZ/100); + return -EIOCBQUEUED; +} + + +static ssize_t sculld_aio_read(struct kiocb *iocb, char __user *buf, size_t count, + loff_t pos) +{ + return sculld_defer_op(0, iocb, buf, count, pos); +} + +static ssize_t sculld_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos) +{ + return sculld_defer_op(1, iocb, (char __user *) buf, count, pos); +} + + + +/* + * Mmap *is* available, but confined in a different file + */ +extern int sculld_mmap(struct file *filp, struct vm_area_struct *vma); + + +/* + * The fops + */ + +struct file_operations sculld_fops = { + .owner = THIS_MODULE, + .llseek = sculld_llseek, + .read = sculld_read, + .write = sculld_write, + .ioctl = sculld_ioctl, + .mmap = sculld_mmap, + .open = sculld_open, + .release = sculld_release, + .aio_read = sculld_aio_read, + .aio_write = sculld_aio_write, +}; + +int sculld_trim(struct sculld_dev *dev) +{ + struct sculld_dev *next, *dptr; + int qset = dev->qset; /* "dev" is not-null */ + int i; + + if (dev->vmas) /* don't trim: there are active mappings */ + return -EBUSY; + + for (dptr = dev; dptr; dptr = next) { /* all the list items */ + if (dptr->data) { + /* This code frees a whole quantum-set */ + for (i = 0; i < qset; i++) + if (dptr->data[i]) + free_pages((unsigned long)(dptr->data[i]), + dptr->order); + + kfree(dptr->data); + dptr->data=NULL; + } + next=dptr->next; + if (dptr != dev) kfree(dptr); /* all of them but the first */ + } + dev->size = 0; + dev->qset = sculld_qset; + dev->order = sculld_order; + dev->next = NULL; + return 0; +} + + +static void sculld_setup_cdev(struct sculld_dev *dev, int index) +{ + int err, devno = MKDEV(sculld_major, index); + + cdev_init(&dev->cdev, &sculld_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &sculld_fops; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scull%d", err, index); +} + +static ssize_t sculld_show_dev(struct device *ddev, char *buf) +{ + struct sculld_dev *dev = ddev->driver_data; + + return print_dev_t(buf, dev->cdev.dev); +} + +static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL); + +static void sculld_register_dev(struct sculld_dev *dev, int index) +{ + sprintf(dev->devname, "sculld%d", index); + dev->ldev.name = dev->devname; + dev->ldev.driver = &sculld_driver; + dev->ldev.dev.driver_data = dev; + register_ldd_device(&dev->ldev); + device_create_file(&dev->ldev.dev, &dev_attr_dev); +} + + +/* + * Finally, the module stuff + */ + +int sculld_init(void) +{ + int result, i; + dev_t dev = MKDEV(sculld_major, 0); + + /* + * Register your major, and accept a dynamic number. + */ + if (sculld_major) + result = register_chrdev_region(dev, sculld_devs, "sculld"); + else { + result = alloc_chrdev_region(&dev, 0, sculld_devs, "sculld"); + sculld_major = MAJOR(dev); + } + if (result < 0) + return result; + + /* + * Register with the driver core. + */ + register_ldd_driver(&sculld_driver); + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + sculld_devices = kmalloc(sculld_devs*sizeof (struct sculld_dev), GFP_KERNEL); + if (!sculld_devices) { + result = -ENOMEM; + goto fail_malloc; + } + memset(sculld_devices, 0, sculld_devs*sizeof (struct sculld_dev)); + for (i = 0; i < sculld_devs; i++) { + sculld_devices[i].order = sculld_order; + sculld_devices[i].qset = sculld_qset; + sema_init (&sculld_devices[i].sem, 1); + sculld_setup_cdev(sculld_devices + i, i); + sculld_register_dev(sculld_devices + i, i); + } + + +#ifdef SCULLD_USE_PROC /* only when available */ + create_proc_read_entry("sculldmem", 0, NULL, sculld_read_procmem, NULL); +#endif + return 0; /* succeed */ + + fail_malloc: + unregister_chrdev_region(dev, sculld_devs); + return result; +} + + + +void sculld_cleanup(void) +{ + int i; + +#ifdef SCULLD_USE_PROC + remove_proc_entry("sculldmem", NULL); +#endif + + for (i = 0; i < sculld_devs; i++) { + unregister_ldd_device(&sculld_devices[i].ldev); + cdev_del(&sculld_devices[i].cdev); + sculld_trim(sculld_devices + i); + } + kfree(sculld_devices); + unregister_ldd_driver(&sculld_driver); + unregister_chrdev_region(MKDEV (sculld_major, 0), sculld_devs); +} + + +module_init(sculld_init); +module_exit(sculld_cleanup); diff --git a/examples/sculld/mmap.c b/examples/sculld/mmap.c new file mode 100644 index 0000000..9325997 --- /dev/null +++ b/examples/sculld/mmap.c @@ -0,0 +1,118 @@ +/* -*- C -*- + * mmap.c -- memory mapping for the sculld char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ + */ + +#include +#include + +#include /* everything */ +#include /* error codes */ +#include + +#include "sculld.h" /* local definitions */ + + +/* + * open and close: just keep track of how many times the device is + * mapped, to avoid releasing it. + */ + +void sculld_vma_open(struct vm_area_struct *vma) +{ + struct sculld_dev *dev = vma->vm_private_data; + + dev->vmas++; +} + +void sculld_vma_close(struct vm_area_struct *vma) +{ + struct sculld_dev *dev = vma->vm_private_data; + + dev->vmas--; +} + +/* + * The nopage method: the core of the file. It retrieves the + * page required from the sculld device and returns it to the + * user. The count for the page must be incremented, because + * it is automatically decremented at page unmap. + * + * For this reason, "order" must be zero. Otherwise, only the first + * page has its count incremented, and the allocating module must + * release it as a whole block. Therefore, it isn't possible to map + * pages from a multipage block: when they are unmapped, their count + * is individually decreased, and would drop to 0. + */ + +struct page *sculld_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset; + struct sculld_dev *ptr, *dev = vma->vm_private_data; + struct page *page = NOPAGE_SIGBUS; + void *pageptr = NULL; /* default to "missing" */ + + down(&dev->sem); + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + if (offset >= dev->size) goto out; /* out of range */ + + /* + * Now retrieve the sculld device from the list,then the page. + * If the device has holes, the process receives a SIGBUS when + * accessing the hole. + */ + offset >>= PAGE_SHIFT; /* offset is a number of pages */ + for (ptr = dev; ptr && offset >= dev->qset;) { + ptr = ptr->next; + offset -= dev->qset; + } + if (ptr && ptr->data) pageptr = ptr->data[offset]; + if (!pageptr) goto out; /* hole or end-of-file */ + + /* got it, now increment the count */ + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + out: + up(&dev->sem); + return page; +} + + + +struct vm_operations_struct sculld_vm_ops = { + .open = sculld_vma_open, + .close = sculld_vma_close, + .nopage = sculld_vma_nopage, +}; + + +int sculld_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct inode *inode = filp->f_dentry->d_inode; + + /* refuse to map if order is not 0 */ + if (sculld_devices[iminor(inode)].order) + return -ENODEV; + + /* don't do anything here: "nopage" will set up page table entries */ + vma->vm_ops = &sculld_vm_ops; + vma->vm_flags |= VM_RESERVED; + vma->vm_private_data = filp->private_data; + sculld_vma_open(vma); + return 0; +} + diff --git a/examples/sculld/sculld.h b/examples/sculld/sculld.h new file mode 100644 index 0000000..8c21f75 --- /dev/null +++ b/examples/sculld/sculld.h @@ -0,0 +1,126 @@ +/* -*- C -*- + * sculld.h -- definitions for the sculld char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include +#include +#include "../include/lddbus.h" + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SCULLD_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "sculld: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + +#define SCULLD_MAJOR 0 /* dynamic major by default */ + +#define SCULLD_DEVS 4 /* sculld0 through sculld3 */ + +/* + * The bare device is a variable-length region of memory. + * Use a linked list of indirect blocks. + * + * "sculld_dev->data" points to an array of pointers, each + * pointer refers to a memory page. + * + * The array (quantum-set) is SCULLD_QSET long. + */ +#define SCULLD_ORDER 0 /* one page at a time */ +#define SCULLD_QSET 500 + +struct sculld_dev { + void **data; + struct sculld_dev *next; /* next listitem */ + int vmas; /* active mappings */ + int order; /* the current allocation order */ + int qset; /* the current array size */ + size_t size; /* 32-bit will suffice */ + struct semaphore sem; /* Mutual exclusion */ + struct cdev cdev; + char devname[20]; + struct ldd_device ldev; +}; + +extern struct sculld_dev *sculld_devices; + +extern struct file_operations sculld_fops; + +/* + * The different configurable parameters + */ +extern int sculld_major; /* main.c */ +extern int sculld_devs; +extern int sculld_order; +extern int sculld_qset; + +/* + * Prototypes for shared functions + */ +int sculld_trim(struct sculld_dev *dev); +struct sculld_dev *sculld_follow(struct sculld_dev *dev, int n); + + +#ifdef SCULLD_DEBUG +# define SCULLD_USE_PROC +#endif + +/* + * Ioctl definitions + */ + +/* Use 'K' as magic number */ +#define SCULLD_IOC_MAGIC 'K' + +#define SCULLD_IOCRESET _IO(SCULLD_IOC_MAGIC, 0) + +/* + * S means "Set" through a ptr, + * T means "Tell" directly + * G means "Get" (to a pointed var) + * Q means "Query", response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define SCULLD_IOCSORDER _IOW(SCULLD_IOC_MAGIC, 1, int) +#define SCULLD_IOCTORDER _IO(SCULLD_IOC_MAGIC, 2) +#define SCULLD_IOCGORDER _IOR(SCULLD_IOC_MAGIC, 3, int) +#define SCULLD_IOCQORDER _IO(SCULLD_IOC_MAGIC, 4) +#define SCULLD_IOCXORDER _IOWR(SCULLD_IOC_MAGIC, 5, int) +#define SCULLD_IOCHORDER _IO(SCULLD_IOC_MAGIC, 6) +#define SCULLD_IOCSQSET _IOW(SCULLD_IOC_MAGIC, 7, int) +#define SCULLD_IOCTQSET _IO(SCULLD_IOC_MAGIC, 8) +#define SCULLD_IOCGQSET _IOR(SCULLD_IOC_MAGIC, 9, int) +#define SCULLD_IOCQQSET _IO(SCULLD_IOC_MAGIC, 10) +#define SCULLD_IOCXQSET _IOWR(SCULLD_IOC_MAGIC,11, int) +#define SCULLD_IOCHQSET _IO(SCULLD_IOC_MAGIC, 12) + +#define SCULLD_IOC_MAXNR 12 + + + diff --git a/examples/sculld/sculld_load b/examples/sculld/sculld_load new file mode 100644 index 0000000..14f5541 --- /dev/null +++ b/examples/sculld/sculld_load @@ -0,0 +1,30 @@ +#!/bin/sh +module="sculld" +device="sculld" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# remove stale nodes +rm -f /dev/${device}? + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +ln -sf ${device}0 /dev/${device} + +# give appropriate group/permissions +chgrp $group /dev/${device}[0-3] +chmod $mode /dev/${device}[0-3] diff --git a/examples/sculld/sculld_unload b/examples/sculld/sculld_unload new file mode 100644 index 0000000..edebbf9 --- /dev/null +++ b/examples/sculld/sculld_unload @@ -0,0 +1,11 @@ +#!/bin/sh +module="sculld" +device="sculld" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# remove nodes +rm -f /dev/${device}[0-3] /dev/${device} + +exit 0 diff --git a/examples/scullp/Makefile b/examples/scullp/Makefile new file mode 100644 index 0000000..2a74203 --- /dev/null +++ b/examples/scullp/Makefile @@ -0,0 +1,46 @@ + +# Comment/uncomment the following line to enable/disable debugging +#DEBUG = y + + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULLP_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) -I$(LDDINC) + +TARGET = scullp + +ifneq ($(KERNELRELEASE),) + +scullp-objs := main.o mmap.o + +obj-m := scullp.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD) modules + +endif + + +install: + install -d $(INSTALLDIR) + install -c $(TARGET).o $(INSTALLDIR) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/scullp/main.c b/examples/scullp/main.c new file mode 100644 index 0000000..c48dd79 --- /dev/null +++ b/examples/scullp/main.c @@ -0,0 +1,598 @@ +/* -*- C -*- + * main.c -- the bare scullp char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $ + */ + +#include +#include +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include "scullp.h" /* local definitions */ + + +int scullp_major = SCULLP_MAJOR; +int scullp_devs = SCULLP_DEVS; /* number of bare scullp devices */ +int scullp_qset = SCULLP_QSET; +int scullp_order = SCULLP_ORDER; + +module_param(scullp_major, int, 0); +module_param(scullp_devs, int, 0); +module_param(scullp_qset, int, 0); +module_param(scullp_order, int, 0); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct scullp_dev *scullp_devices; /* allocated in scullp_init */ + +int scullp_trim(struct scullp_dev *dev); +void scullp_cleanup(void); + + + + + + +#ifdef SCULLP_USE_PROC /* don't waste space if unused */ +/* + * The proc filesystem: function to read and entry + */ + +void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len) +{ + if (*offset == 0) + return; + if (*offset >= *len) { + /* Not there yet */ + *offset -= *len; + *len = 0; + } else { + /* We're into the interesting stuff now */ + *start = buf + *offset; + *offset = 0; + } +} + +/* FIXME: Do we need this here?? It be ugly */ +int scullp_read_procmem(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, j, order, qset, len = 0; + int limit = count - 80; /* Don't print more than this */ + struct scullp_dev *d; + + *start = buf; + for(i = 0; i < scullp_devs; i++) { + d = &scullp_devices[i]; + if (down_interruptible (&d->sem)) + return -ERESTARTSYS; + qset = d->qset; /* retrieve the features of each device */ + order = d->order; + len += sprintf(buf+len,"\nDevice %i: qset %i, order %i, sz %li\n", + i, qset, order, (long)(d->size)); + for (; d; d = d->next) { /* scan the list */ + len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data); + scullp_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + if (d->data && !d->next) /* dump only the last item - save space */ + for (j = 0; j < qset; j++) { + if (d->data[j]) + len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]); + scullp_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + } + } + out: + up (&scullp_devices[i].sem); + if (len > limit) + break; + } + *eof = 1; + return len; +} + +#endif /* SCULLP_USE_PROC */ + +/* + * Open and close + */ + +int scullp_open (struct inode *inode, struct file *filp) +{ + struct scullp_dev *dev; /* device information */ + + /* Find the device */ + dev = container_of(inode->i_cdev, struct scullp_dev, cdev); + + /* now trim to 0 the length of the device if open was write-only */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + scullp_trim(dev); /* ignore errors */ + up (&dev->sem); + } + + /* and use filp->private_data to point to the device data */ + filp->private_data = dev; + + return 0; /* success */ +} + +int scullp_release (struct inode *inode, struct file *filp) +{ + return 0; +} + +/* + * Follow the list + */ +struct scullp_dev *scullp_follow(struct scullp_dev *dev, int n) +{ + while (n--) { + if (!dev->next) { + dev->next = kmalloc(sizeof(struct scullp_dev), GFP_KERNEL); + memset(dev->next, 0, sizeof(struct scullp_dev)); + } + dev = dev->next; + continue; + } + return dev; +} + +/* + * Data management: read and write + */ + +ssize_t scullp_read (struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullp_dev *dev = filp->private_data; /* the first listitem */ + struct scullp_dev *dptr; + int quantum = PAGE_SIZE << dev->order; + int qset = dev->qset; + int itemsize = quantum * qset; /* how many bytes in the listitem */ + int item, s_pos, q_pos, rest; + ssize_t retval = 0; + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + if (*f_pos > dev->size) + goto nothing; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + /* find listitem, qset index, and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position (defined elsewhere) */ + dptr = scullp_follow(dev, item); + + if (!dptr->data) + goto nothing; /* don't fill holes */ + if (!dptr->data[s_pos]) + goto nothing; + if (count > quantum - q_pos) + count = quantum - q_pos; /* read only up to the end of this quantum */ + + if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) { + retval = -EFAULT; + goto nothing; + } + up (&dev->sem); + + *f_pos += count; + return count; + + nothing: + up (&dev->sem); + return retval; +} + + + +ssize_t scullp_write (struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullp_dev *dev = filp->private_data; + struct scullp_dev *dptr; + int quantum = PAGE_SIZE << dev->order; + int qset = dev->qset; + int itemsize = quantum * qset; + int item, s_pos, q_pos, rest; + ssize_t retval = -ENOMEM; /* our most likely error */ + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + + /* find listitem, qset index and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ + dptr = scullp_follow(dev, item); + if (!dptr->data) { + dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL); + if (!dptr->data) + goto nomem; + memset(dptr->data, 0, qset * sizeof(char *)); + } + /* Here's the allocation of a single quantum */ + if (!dptr->data[s_pos]) { + dptr->data[s_pos] = + (void *)__get_free_pages(GFP_KERNEL, dptr->order); + if (!dptr->data[s_pos]) + goto nomem; + memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order); + } + if (count > quantum - q_pos) + count = quantum - q_pos; /* write only up to the end of this quantum */ + if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) { + retval = -EFAULT; + goto nomem; + } + *f_pos += count; + + /* update the size */ + if (dev->size < *f_pos) + dev->size = *f_pos; + up (&dev->sem); + return count; + + nomem: + up (&dev->sem); + return retval; +} + +/* + * The ioctl() implementation + */ + +int scullp_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int err = 0, ret = 0, tmp; + + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (_IOC_TYPE(cmd) != SCULLP_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SCULLP_IOC_MAXNR) return -ENOTTY; + + /* + * the type is a bitmask, and VERIFY_WRITE catches R/W + * transfers. Note that the type is user-oriented, while + * verify_area is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch(cmd) { + + case SCULLP_IOCRESET: + scullp_qset = SCULLP_QSET; + scullp_order = SCULLP_ORDER; + break; + + case SCULLP_IOCSORDER: /* Set: arg points to the value */ + ret = __get_user(scullp_order, (int __user *) arg); + break; + + case SCULLP_IOCTORDER: /* Tell: arg is the value */ + scullp_order = arg; + break; + + case SCULLP_IOCGORDER: /* Get: arg is pointer to result */ + ret = __put_user (scullp_order, (int __user *) arg); + break; + + case SCULLP_IOCQORDER: /* Query: return it (it's positive) */ + return scullp_order; + + case SCULLP_IOCXORDER: /* eXchange: use arg as pointer */ + tmp = scullp_order; + ret = __get_user(scullp_order, (int __user *) arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *) arg); + break; + + case SCULLP_IOCHORDER: /* sHift: like Tell + Query */ + tmp = scullp_order; + scullp_order = arg; + return tmp; + + case SCULLP_IOCSQSET: + ret = __get_user(scullp_qset, (int __user *) arg); + break; + + case SCULLP_IOCTQSET: + scullp_qset = arg; + break; + + case SCULLP_IOCGQSET: + ret = __put_user(scullp_qset, (int __user *)arg); + break; + + case SCULLP_IOCQQSET: + return scullp_qset; + + case SCULLP_IOCXQSET: + tmp = scullp_qset; + ret = __get_user(scullp_qset, (int __user *)arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *)arg); + break; + + case SCULLP_IOCHQSET: + tmp = scullp_qset; + scullp_qset = arg; + return tmp; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return ret; +} + +/* + * The "extended" operations + */ + +loff_t scullp_llseek (struct file *filp, loff_t off, int whence) +{ + struct scullp_dev *dev = filp->private_data; + long newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happen */ + return -EINVAL; + } + if (newpos<0) return -EINVAL; + filp->f_pos = newpos; + return newpos; +} + + +/* + * A simple asynchronous I/O implementation. + */ + +struct async_work { + struct kiocb *iocb; + int result; + struct work_struct work; +}; + +/* + * "Complete" an asynchronous operation. + */ +static void scullp_do_deferred_op(void *p) +{ + struct async_work *stuff = (struct async_work *) p; + aio_complete(stuff->iocb, stuff->result, 0); + kfree(stuff); +} + + +static int scullp_defer_op(int write, struct kiocb *iocb, char __user *buf, + size_t count, loff_t pos) +{ + struct async_work *stuff; + int result; + + /* Copy now while we can access the buffer */ + if (write) + result = scullp_write(iocb->ki_filp, buf, count, &pos); + else + result = scullp_read(iocb->ki_filp, buf, count, &pos); + + /* If this is a synchronous IOCB, we return our status now. */ + if (is_sync_kiocb(iocb)) + return result; + + /* Otherwise defer the completion for a few milliseconds. */ + stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); + if (stuff == NULL) + return result; /* No memory, just complete now */ + stuff->iocb = iocb; + stuff->result = result; + INIT_WORK(&stuff->work, scullp_do_deferred_op, stuff); + schedule_delayed_work(&stuff->work, HZ/100); + return -EIOCBQUEUED; +} + + +static ssize_t scullp_aio_read(struct kiocb *iocb, char __user *buf, size_t count, + loff_t pos) +{ + return scullp_defer_op(0, iocb, buf, count, pos); +} + +static ssize_t scullp_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos) +{ + return scullp_defer_op(1, iocb, (char __user *) buf, count, pos); +} + + + +/* + * Mmap *is* available, but confined in a different file + */ +extern int scullp_mmap(struct file *filp, struct vm_area_struct *vma); + + +/* + * The fops + */ + +struct file_operations scullp_fops = { + .owner = THIS_MODULE, + .llseek = scullp_llseek, + .read = scullp_read, + .write = scullp_write, + .ioctl = scullp_ioctl, + .mmap = scullp_mmap, + .open = scullp_open, + .release = scullp_release, + .aio_read = scullp_aio_read, + .aio_write = scullp_aio_write, +}; + +int scullp_trim(struct scullp_dev *dev) +{ + struct scullp_dev *next, *dptr; + int qset = dev->qset; /* "dev" is not-null */ + int i; + + if (dev->vmas) /* don't trim: there are active mappings */ + return -EBUSY; + + for (dptr = dev; dptr; dptr = next) { /* all the list items */ + if (dptr->data) { + /* This code frees a whole quantum-set */ + for (i = 0; i < qset; i++) + if (dptr->data[i]) + free_pages((unsigned long)(dptr->data[i]), + dptr->order); + + kfree(dptr->data); + dptr->data=NULL; + } + next=dptr->next; + if (dptr != dev) kfree(dptr); /* all of them but the first */ + } + dev->size = 0; + dev->qset = scullp_qset; + dev->order = scullp_order; + dev->next = NULL; + return 0; +} + + +static void scullp_setup_cdev(struct scullp_dev *dev, int index) +{ + int err, devno = MKDEV(scullp_major, index); + + cdev_init(&dev->cdev, &scullp_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &scullp_fops; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scull%d", err, index); +} + + + +/* + * Finally, the module stuff + */ + +int scullp_init(void) +{ + int result, i; + dev_t dev = MKDEV(scullp_major, 0); + + /* + * Register your major, and accept a dynamic number. + */ + if (scullp_major) + result = register_chrdev_region(dev, scullp_devs, "scullp"); + else { + result = alloc_chrdev_region(&dev, 0, scullp_devs, "scullp"); + scullp_major = MAJOR(dev); + } + if (result < 0) + return result; + + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + scullp_devices = kmalloc(scullp_devs*sizeof (struct scullp_dev), GFP_KERNEL); + if (!scullp_devices) { + result = -ENOMEM; + goto fail_malloc; + } + memset(scullp_devices, 0, scullp_devs*sizeof (struct scullp_dev)); + for (i = 0; i < scullp_devs; i++) { + scullp_devices[i].order = scullp_order; + scullp_devices[i].qset = scullp_qset; + sema_init (&scullp_devices[i].sem, 1); + scullp_setup_cdev(scullp_devices + i, i); + } + + +#ifdef SCULLP_USE_PROC /* only when available */ + create_proc_read_entry("scullpmem", 0, NULL, scullp_read_procmem, NULL); +#endif + return 0; /* succeed */ + + fail_malloc: + unregister_chrdev_region(dev, scullp_devs); + return result; +} + + + +void scullp_cleanup(void) +{ + int i; + +#ifdef SCULLP_USE_PROC + remove_proc_entry("scullpmem", NULL); +#endif + + for (i = 0; i < scullp_devs; i++) { + cdev_del(&scullp_devices[i].cdev); + scullp_trim(scullp_devices + i); + } + kfree(scullp_devices); + unregister_chrdev_region(MKDEV (scullp_major, 0), scullp_devs); +} + + +module_init(scullp_init); +module_exit(scullp_cleanup); diff --git a/examples/scullp/mmap.c b/examples/scullp/mmap.c new file mode 100644 index 0000000..f5166db --- /dev/null +++ b/examples/scullp/mmap.c @@ -0,0 +1,119 @@ +/* -*- C -*- + * mmap.c -- memory mapping for the scullp char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ + */ + +#include +#include + +#include /* everything */ +#include /* error codes */ +#include + +#include "scullp.h" /* local definitions */ + + +/* + * open and close: just keep track of how many times the device is + * mapped, to avoid releasing it. + */ + +void scullp_vma_open(struct vm_area_struct *vma) +{ + struct scullp_dev *dev = vma->vm_private_data; + + dev->vmas++; +} + +void scullp_vma_close(struct vm_area_struct *vma) +{ + struct scullp_dev *dev = vma->vm_private_data; + + dev->vmas--; +} + +/* + * The nopage method: the core of the file. It retrieves the + * page required from the scullp device and returns it to the + * user. The count for the page must be incremented, because + * it is automatically decremented at page unmap. + * + * For this reason, "order" must be zero. Otherwise, only the first + * page has its count incremented, and the allocating module must + * release it as a whole block. Therefore, it isn't possible to map + * pages from a multipage block: when they are unmapped, their count + * is individually decreased, and would drop to 0. + */ + +struct page *scullp_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset; + struct scullp_dev *ptr, *dev = vma->vm_private_data; + struct page *page = NOPAGE_SIGBUS; + void *pageptr = NULL; /* default to "missing" */ + + down(&dev->sem); + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + if (offset >= dev->size) goto out; /* out of range */ + + /* + * Now retrieve the scullp device from the list,then the page. + * If the device has holes, the process receives a SIGBUS when + * accessing the hole. + */ + offset >>= PAGE_SHIFT; /* offset is a number of pages */ + for (ptr = dev; ptr && offset >= dev->qset;) { + ptr = ptr->next; + offset -= dev->qset; + } + if (ptr && ptr->data) pageptr = ptr->data[offset]; + if (!pageptr) goto out; /* hole or end-of-file */ + page = virt_to_page(pageptr); + + /* got it, now increment the count */ + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + out: + up(&dev->sem); + return page; +} + + + +struct vm_operations_struct scullp_vm_ops = { + .open = scullp_vma_open, + .close = scullp_vma_close, + .nopage = scullp_vma_nopage, +}; + + +int scullp_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct inode *inode = filp->f_dentry->d_inode; + + /* refuse to map if order is not 0 */ + if (scullp_devices[iminor(inode)].order) + return -ENODEV; + + /* don't do anything here: "nopage" will set up page table entries */ + vma->vm_ops = &scullp_vm_ops; + vma->vm_flags |= VM_RESERVED; + vma->vm_private_data = filp->private_data; + scullp_vma_open(vma); + return 0; +} + diff --git a/examples/scullp/scullp.h b/examples/scullp/scullp.h new file mode 100644 index 0000000..949bd25 --- /dev/null +++ b/examples/scullp/scullp.h @@ -0,0 +1,122 @@ +/* -*- C -*- + * scullp.h -- definitions for the scullp char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SCULLP_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullp: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + +#define SCULLP_MAJOR 0 /* dynamic major by default */ + +#define SCULLP_DEVS 4 /* scullp0 through scullp3 */ + +/* + * The bare device is a variable-length region of memory. + * Use a linked list of indirect blocks. + * + * "scullp_dev->data" points to an array of pointers, each + * pointer refers to a memory page. + * + * The array (quantum-set) is SCULLP_QSET long. + */ +#define SCULLP_ORDER 0 /* one page at a time */ +#define SCULLP_QSET 500 + +struct scullp_dev { + void **data; + struct scullp_dev *next; /* next listitem */ + int vmas; /* active mappings */ + int order; /* the current allocation order */ + int qset; /* the current array size */ + size_t size; /* 32-bit will suffice */ + struct semaphore sem; /* Mutual exclusion */ + struct cdev cdev; +}; + +extern struct scullp_dev *scullp_devices; + +extern struct file_operations scullp_fops; + +/* + * The different configurable parameters + */ +extern int scullp_major; /* main.c */ +extern int scullp_devs; +extern int scullp_order; +extern int scullp_qset; + +/* + * Prototypes for shared functions + */ +int scullp_trim(struct scullp_dev *dev); +struct scullp_dev *scullp_follow(struct scullp_dev *dev, int n); + + +#ifdef SCULLP_DEBUG +# define SCULLP_USE_PROC +#endif + +/* + * Ioctl definitions + */ + +/* Use 'K' as magic number */ +#define SCULLP_IOC_MAGIC 'K' + +#define SCULLP_IOCRESET _IO(SCULLP_IOC_MAGIC, 0) + +/* + * S means "Set" through a ptr, + * T means "Tell" directly + * G means "Get" (to a pointed var) + * Q means "Query", response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define SCULLP_IOCSORDER _IOW(SCULLP_IOC_MAGIC, 1, int) +#define SCULLP_IOCTORDER _IO(SCULLP_IOC_MAGIC, 2) +#define SCULLP_IOCGORDER _IOR(SCULLP_IOC_MAGIC, 3, int) +#define SCULLP_IOCQORDER _IO(SCULLP_IOC_MAGIC, 4) +#define SCULLP_IOCXORDER _IOWR(SCULLP_IOC_MAGIC, 5, int) +#define SCULLP_IOCHORDER _IO(SCULLP_IOC_MAGIC, 6) +#define SCULLP_IOCSQSET _IOW(SCULLP_IOC_MAGIC, 7, int) +#define SCULLP_IOCTQSET _IO(SCULLP_IOC_MAGIC, 8) +#define SCULLP_IOCGQSET _IOR(SCULLP_IOC_MAGIC, 9, int) +#define SCULLP_IOCQQSET _IO(SCULLP_IOC_MAGIC, 10) +#define SCULLP_IOCXQSET _IOWR(SCULLP_IOC_MAGIC,11, int) +#define SCULLP_IOCHQSET _IO(SCULLP_IOC_MAGIC, 12) + +#define SCULLP_IOC_MAXNR 12 + + + diff --git a/examples/scullp/scullp_load b/examples/scullp/scullp_load new file mode 100644 index 0000000..d7dc1ef --- /dev/null +++ b/examples/scullp/scullp_load @@ -0,0 +1,30 @@ +#!/bin/sh +module="scullp" +device="scullp" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# remove stale nodes +rm -f /dev/${device}? + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +ln -sf ${device}0 /dev/${device} + +# give appropriate group/permissions +chgrp $group /dev/${device}[0-3] +chmod $mode /dev/${device}[0-3] diff --git a/examples/scullp/scullp_unload b/examples/scullp/scullp_unload new file mode 100644 index 0000000..adf5e2c --- /dev/null +++ b/examples/scullp/scullp_unload @@ -0,0 +1,11 @@ +#!/bin/sh +module="scullp" +device="scullp" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# remove nodes +rm -f /dev/${device}[0-3] /dev/${device} + +exit 0 diff --git a/examples/scullv/Makefile b/examples/scullv/Makefile new file mode 100644 index 0000000..6dea81f --- /dev/null +++ b/examples/scullv/Makefile @@ -0,0 +1,46 @@ + +# Comment/uncomment the following line to enable/disable debugging +#DEBUG = y + + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULLV_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) -I$(LDDINC) + +TARGET = scullv + +ifneq ($(KERNELRELEASE),) + +scullv-objs := main.o mmap.o + +obj-m := scullv.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +modules: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD) modules + +endif + + +install: + install -d $(INSTALLDIR) + install -c $(TARGET).o $(INSTALLDIR) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/scullv/main.c b/examples/scullv/main.c new file mode 100644 index 0000000..30b57d8 --- /dev/null +++ b/examples/scullv/main.c @@ -0,0 +1,597 @@ +/* -*- C -*- + * main.c -- the bare scullv char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $ + */ + +#include +#include +#include +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include /* O_ACCMODE */ +#include +#include +#include +#include "scullv.h" /* local definitions */ + + +int scullv_major = SCULLV_MAJOR; +int scullv_devs = SCULLV_DEVS; /* number of bare scullv devices */ +int scullv_qset = SCULLV_QSET; +int scullv_order = SCULLV_ORDER; + +module_param(scullv_major, int, 0); +module_param(scullv_devs, int, 0); +module_param(scullv_qset, int, 0); +module_param(scullv_order, int, 0); +MODULE_AUTHOR("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct scullv_dev *scullv_devices; /* allocated in scullv_init */ + +int scullv_trim(struct scullv_dev *dev); +void scullv_cleanup(void); + + + + + + +#ifdef SCULLV_USE_PROC /* don't waste space if unused */ +/* + * The proc filesystem: function to read and entry + */ + +void scullv_proc_offset(char *buf, char **start, off_t *offset, int *len) +{ + if (*offset == 0) + return; + if (*offset >= *len) { + /* Not there yet */ + *offset -= *len; + *len = 0; + } else { + /* We're into the interesting stuff now */ + *start = buf + *offset; + *offset = 0; + } +} + +/* FIXME: Do we need this here?? It be ugly */ +int scullv_read_procmem(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, j, order, qset, len = 0; + int limit = count - 80; /* Don't print more than this */ + struct scullv_dev *d; + + *start = buf; + for(i = 0; i < scullv_devs; i++) { + d = &scullv_devices[i]; + if (down_interruptible (&d->sem)) + return -ERESTARTSYS; + qset = d->qset; /* retrieve the features of each device */ + order = d->order; + len += sprintf(buf+len,"\nDevice %i: qset %i, order %i, sz %li\n", + i, qset, order, (long)(d->size)); + for (; d; d = d->next) { /* scan the list */ + len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data); + scullv_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + if (d->data && !d->next) /* dump only the last item - save space */ + for (j = 0; j < qset; j++) { + if (d->data[j]) + len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]); + scullv_proc_offset (buf, start, &offset, &len); + if (len > limit) + goto out; + } + } + out: + up (&scullv_devices[i].sem); + if (len > limit) + break; + } + *eof = 1; + return len; +} + +#endif /* SCULLV_USE_PROC */ + +/* + * Open and close + */ + +int scullv_open (struct inode *inode, struct file *filp) +{ + struct scullv_dev *dev; /* device information */ + + /* Find the device */ + dev = container_of(inode->i_cdev, struct scullv_dev, cdev); + + /* now trim to 0 the length of the device if open was write-only */ + if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + scullv_trim(dev); /* ignore errors */ + up (&dev->sem); + } + + /* and use filp->private_data to point to the device data */ + filp->private_data = dev; + + return 0; /* success */ +} + +int scullv_release (struct inode *inode, struct file *filp) +{ + return 0; +} + +/* + * Follow the list + */ +struct scullv_dev *scullv_follow(struct scullv_dev *dev, int n) +{ + while (n--) { + if (!dev->next) { + dev->next = kmalloc(sizeof(struct scullv_dev), GFP_KERNEL); + memset(dev->next, 0, sizeof(struct scullv_dev)); + } + dev = dev->next; + continue; + } + return dev; +} + +/* + * Data management: read and write + */ + +ssize_t scullv_read (struct file *filp, char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullv_dev *dev = filp->private_data; /* the first listitem */ + struct scullv_dev *dptr; + int quantum = PAGE_SIZE << dev->order; + int qset = dev->qset; + int itemsize = quantum * qset; /* how many bytes in the listitem */ + int item, s_pos, q_pos, rest; + ssize_t retval = 0; + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + if (*f_pos > dev->size) + goto nothing; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + /* find listitem, qset index, and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position (defined elsewhere) */ + dptr = scullv_follow(dev, item); + + if (!dptr->data) + goto nothing; /* don't fill holes */ + if (!dptr->data[s_pos]) + goto nothing; + if (count > quantum - q_pos) + count = quantum - q_pos; /* read only up to the end of this quantum */ + + if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) { + retval = -EFAULT; + goto nothing; + } + up (&dev->sem); + + *f_pos += count; + return count; + + nothing: + up (&dev->sem); + return retval; +} + + + +ssize_t scullv_write (struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + struct scullv_dev *dev = filp->private_data; + struct scullv_dev *dptr; + int quantum = PAGE_SIZE << dev->order; + int qset = dev->qset; + int itemsize = quantum * qset; + int item, s_pos, q_pos, rest; + ssize_t retval = -ENOMEM; /* our most likely error */ + + if (down_interruptible (&dev->sem)) + return -ERESTARTSYS; + + /* find listitem, qset index and offset in the quantum */ + item = ((long) *f_pos) / itemsize; + rest = ((long) *f_pos) % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ + dptr = scullv_follow(dev, item); + if (!dptr->data) { + dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL); + if (!dptr->data) + goto nomem; + memset(dptr->data, 0, qset * sizeof(char *)); + } + /* Allocate a quantum using virtual addresses */ + if (!dptr->data[s_pos]) { + dptr->data[s_pos] = (void *)vmalloc(PAGE_SIZE << dptr->order); + if (!dptr->data[s_pos]) + goto nomem; + memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order); + } + if (count > quantum - q_pos) + count = quantum - q_pos; /* write only up to the end of this quantum */ + if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) { + retval = -EFAULT; + goto nomem; + } + *f_pos += count; + + /* update the size */ + if (dev->size < *f_pos) + dev->size = *f_pos; + up (&dev->sem); + return count; + + nomem: + up (&dev->sem); + return retval; +} + +/* + * The ioctl() implementation + */ + +int scullv_ioctl (struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int err = 0, ret = 0, tmp; + + /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */ + if (_IOC_TYPE(cmd) != SCULLV_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > SCULLV_IOC_MAXNR) return -ENOTTY; + + /* + * the type is a bitmask, and VERIFY_WRITE catches R/W + * transfers. Note that the type is user-oriented, while + * verify_area is kernel-oriented, so the concept of "read" and + * "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch(cmd) { + + case SCULLV_IOCRESET: + scullv_qset = SCULLV_QSET; + scullv_order = SCULLV_ORDER; + break; + + case SCULLV_IOCSORDER: /* Set: arg points to the value */ + ret = __get_user(scullv_order, (int __user *) arg); + break; + + case SCULLV_IOCTORDER: /* Tell: arg is the value */ + scullv_order = arg; + break; + + case SCULLV_IOCGORDER: /* Get: arg is pointer to result */ + ret = __put_user (scullv_order, (int __user *) arg); + break; + + case SCULLV_IOCQORDER: /* Query: return it (it's positive) */ + return scullv_order; + + case SCULLV_IOCXORDER: /* eXchange: use arg as pointer */ + tmp = scullv_order; + ret = __get_user(scullv_order, (int __user *) arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *) arg); + break; + + case SCULLV_IOCHORDER: /* sHift: like Tell + Query */ + tmp = scullv_order; + scullv_order = arg; + return tmp; + + case SCULLV_IOCSQSET: + ret = __get_user(scullv_qset, (int __user *) arg); + break; + + case SCULLV_IOCTQSET: + scullv_qset = arg; + break; + + case SCULLV_IOCGQSET: + ret = __put_user(scullv_qset, (int __user *)arg); + break; + + case SCULLV_IOCQQSET: + return scullv_qset; + + case SCULLV_IOCXQSET: + tmp = scullv_qset; + ret = __get_user(scullv_qset, (int __user *)arg); + if (ret == 0) + ret = __put_user(tmp, (int __user *)arg); + break; + + case SCULLV_IOCHQSET: + tmp = scullv_qset; + scullv_qset = arg; + return tmp; + + default: /* redundant, as cmd was checked against MAXNR */ + return -ENOTTY; + } + + return ret; +} + +/* + * The "extended" operations + */ + +loff_t scullv_llseek (struct file *filp, loff_t off, int whence) +{ + struct scullv_dev *dev = filp->private_data; + long newpos; + + switch(whence) { + case 0: /* SEEK_SET */ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = dev->size + off; + break; + + default: /* can't happen */ + return -EINVAL; + } + if (newpos<0) return -EINVAL; + filp->f_pos = newpos; + return newpos; +} + + +/* + * A simple asynchronous I/O implementation. + */ + +struct async_work { + struct kiocb *iocb; + int result; + struct work_struct work; +}; + +/* + * "Complete" an asynchronous operation. + */ +static void scullv_do_deferred_op(void *p) +{ + struct async_work *stuff = (struct async_work *) p; + aio_complete(stuff->iocb, stuff->result, 0); + kfree(stuff); +} + + +static int scullv_defer_op(int write, struct kiocb *iocb, char __user *buf, + size_t count, loff_t pos) +{ + struct async_work *stuff; + int result; + + /* Copy now while we can access the buffer */ + if (write) + result = scullv_write(iocb->ki_filp, buf, count, &pos); + else + result = scullv_read(iocb->ki_filp, buf, count, &pos); + + /* If this is a synchronous IOCB, we return our status now. */ + if (is_sync_kiocb(iocb)) + return result; + + /* Otherwise defer the completion for a few milliseconds. */ + stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); + if (stuff == NULL) + return result; /* No memory, just complete now */ + stuff->iocb = iocb; + stuff->result = result; + INIT_WORK(&stuff->work, scullv_do_deferred_op, stuff); + schedule_delayed_work(&stuff->work, HZ/100); + return -EIOCBQUEUED; +} + + +static ssize_t scullv_aio_read(struct kiocb *iocb, char __user *buf, size_t count, + loff_t pos) +{ + return scullv_defer_op(0, iocb, buf, count, pos); +} + +static ssize_t scullv_aio_write(struct kiocb *iocb, const char __user *buf, + size_t count, loff_t pos) +{ + return scullv_defer_op(1, iocb, (char __user *) buf, count, pos); +} + + + +/* + * Mmap *is* available, but confined in a different file + */ +extern int scullv_mmap(struct file *filp, struct vm_area_struct *vma); + + +/* + * The fops + */ + +struct file_operations scullv_fops = { + .owner = THIS_MODULE, + .llseek = scullv_llseek, + .read = scullv_read, + .write = scullv_write, + .ioctl = scullv_ioctl, + .mmap = scullv_mmap, + .open = scullv_open, + .release = scullv_release, + .aio_read = scullv_aio_read, + .aio_write = scullv_aio_write, +}; + +int scullv_trim(struct scullv_dev *dev) +{ + struct scullv_dev *next, *dptr; + int qset = dev->qset; /* "dev" is not-null */ + int i; + + if (dev->vmas) /* don't trim: there are active mappings */ + return -EBUSY; + + for (dptr = dev; dptr; dptr = next) { /* all the list items */ + if (dptr->data) { + /* Release the quantum-set */ + for (i = 0; i < qset; i++) + if (dptr->data[i]) + vfree(dptr->data[i]); + + kfree(dptr->data); + dptr->data=NULL; + } + next=dptr->next; + if (dptr != dev) kfree(dptr); /* all of them but the first */ + } + dev->size = 0; + dev->qset = scullv_qset; + dev->order = scullv_order; + dev->next = NULL; + return 0; +} + + +static void scullv_setup_cdev(struct scullv_dev *dev, int index) +{ + int err, devno = MKDEV(scullv_major, index); + + cdev_init(&dev->cdev, &scullv_fops); + dev->cdev.owner = THIS_MODULE; + dev->cdev.ops = &scullv_fops; + err = cdev_add (&dev->cdev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk(KERN_NOTICE "Error %d adding scull%d", err, index); +} + + + +/* + * Finally, the module stuff + */ + +int scullv_init(void) +{ + int result, i; + dev_t dev = MKDEV(scullv_major, 0); + + /* + * Register your major, and accept a dynamic number. + */ + if (scullv_major) + result = register_chrdev_region(dev, scullv_devs, "scullv"); + else { + result = alloc_chrdev_region(&dev, 0, scullv_devs, "scullv"); + scullv_major = MAJOR(dev); + } + if (result < 0) + return result; + + + /* + * allocate the devices -- we can't have them static, as the number + * can be specified at load time + */ + scullv_devices = kmalloc(scullv_devs*sizeof (struct scullv_dev), GFP_KERNEL); + if (!scullv_devices) { + result = -ENOMEM; + goto fail_malloc; + } + memset(scullv_devices, 0, scullv_devs*sizeof (struct scullv_dev)); + for (i = 0; i < scullv_devs; i++) { + scullv_devices[i].order = scullv_order; + scullv_devices[i].qset = scullv_qset; + sema_init (&scullv_devices[i].sem, 1); + scullv_setup_cdev(scullv_devices + i, i); + } + + +#ifdef SCULLV_USE_PROC /* only when available */ + create_proc_read_entry("scullvmem", 0, NULL, scullv_read_procmem, NULL); +#endif + return 0; /* succeed */ + + fail_malloc: + unregister_chrdev_region(dev, scullv_devs); + return result; +} + + + +void scullv_cleanup(void) +{ + int i; + +#ifdef SCULLV_USE_PROC + remove_proc_entry("scullvmem", NULL); +#endif + + for (i = 0; i < scullv_devs; i++) { + cdev_del(&scullv_devices[i].cdev); + scullv_trim(scullv_devices + i); + } + kfree(scullv_devices); + unregister_chrdev_region(MKDEV (scullv_major, 0), scullv_devs); +} + + +module_init(scullv_init); +module_exit(scullv_cleanup); diff --git a/examples/scullv/mmap.c b/examples/scullv/mmap.c new file mode 100644 index 0000000..0f6656f --- /dev/null +++ b/examples/scullv/mmap.c @@ -0,0 +1,120 @@ +/* -*- C -*- + * mmap.c -- memory mapping for the scullv char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ + */ + +#include +#include + +#include /* everything */ +#include /* error codes */ +#include + +#include "scullv.h" /* local definitions */ + + +/* + * open and close: just keep track of how many times the device is + * mapped, to avoid releasing it. + */ + +void scullv_vma_open(struct vm_area_struct *vma) +{ + struct scullv_dev *dev = vma->vm_private_data; + + dev->vmas++; +} + +void scullv_vma_close(struct vm_area_struct *vma) +{ + struct scullv_dev *dev = vma->vm_private_data; + + dev->vmas--; +} + +/* + * The nopage method: the core of the file. It retrieves the + * page required from the scullv device and returns it to the + * user. The count for the page must be incremented, because + * it is automatically decremented at page unmap. + * + * For this reason, "order" must be zero. Otherwise, only the first + * page has its count incremented, and the allocating module must + * release it as a whole block. Therefore, it isn't possible to map + * pages from a multipage block: when they are unmapped, their count + * is individually decreased, and would drop to 0. + */ + +struct page *scullv_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + unsigned long offset; + struct scullv_dev *ptr, *dev = vma->vm_private_data; + struct page *page = NOPAGE_SIGBUS; + void *pageptr = NULL; /* default to "missing" */ + + down(&dev->sem); + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + if (offset >= dev->size) goto out; /* out of range */ + + /* + * Now retrieve the scullv device from the list,then the page. + * If the device has holes, the process receives a SIGBUS when + * accessing the hole. + */ + offset >>= PAGE_SHIFT; /* offset is a number of pages */ + for (ptr = dev; ptr && offset >= dev->qset;) { + ptr = ptr->next; + offset -= dev->qset; + } + if (ptr && ptr->data) pageptr = ptr->data[offset]; + if (!pageptr) goto out; /* hole or end-of-file */ + + /* + * After scullv lookup, "page" is now the address of the page + * needed by the current process. Since it's a vmalloc address, + * turn it into a struct page. + */ + page = vmalloc_to_page(pageptr); + + /* got it, now increment the count */ + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + out: + up(&dev->sem); + return page; +} + + + +struct vm_operations_struct scullv_vm_ops = { + .open = scullv_vma_open, + .close = scullv_vma_close, + .nopage = scullv_vma_nopage, +}; + + +int scullv_mmap(struct file *filp, struct vm_area_struct *vma) +{ + + /* don't do anything here: "nopage" will set up page table entries */ + vma->vm_ops = &scullv_vm_ops; + vma->vm_flags |= VM_RESERVED; + vma->vm_private_data = filp->private_data; + scullv_vma_open(vma); + return 0; +} + diff --git a/examples/scullv/scullv.h b/examples/scullv/scullv.h new file mode 100644 index 0000000..8ba2595 --- /dev/null +++ b/examples/scullv/scullv.h @@ -0,0 +1,122 @@ +/* -*- C -*- + * scullv.h -- definitions for the scullv char module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +#include +#include + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SCULLV_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullv: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + +#define SCULLV_MAJOR 0 /* dynamic major by default */ + +#define SCULLV_DEVS 4 /* scullv0 through scullv3 */ + +/* + * The bare device is a variable-length region of memory. + * Use a linked list of indirect blocks. + * + * "scullv_dev->data" points to an array of pointers, each + * pointer refers to a memory page. + * + * The array (quantum-set) is SCULLV_QSET long. + */ +#define SCULLV_ORDER 4 /* 16 pages at a time */ +#define SCULLV_QSET 500 + +struct scullv_dev { + void **data; + struct scullv_dev *next; /* next listitem */ + int vmas; /* active mappings */ + int order; /* the current allocation order */ + int qset; /* the current array size */ + size_t size; /* 32-bit will suffice */ + struct semaphore sem; /* Mutual exclusion */ + struct cdev cdev; +}; + +extern struct scullv_dev *scullv_devices; + +extern struct file_operations scullv_fops; + +/* + * The different configurable parameters + */ +extern int scullv_major; /* main.c */ +extern int scullv_devs; +extern int scullv_order; +extern int scullv_qset; + +/* + * Prototypes for shared functions + */ +int scullv_trim(struct scullv_dev *dev); +struct scullv_dev *scullv_follow(struct scullv_dev *dev, int n); + + +#ifdef SCULLV_DEBUG +# define SCULLV_USE_PROC +#endif + +/* + * Ioctl definitions + */ + +/* Use 'K' as magic number */ +#define SCULLV_IOC_MAGIC 'K' + +#define SCULLV_IOCRESET _IO(SCULLV_IOC_MAGIC, 0) + +/* + * S means "Set" through a ptr, + * T means "Tell" directly + * G means "Get" (to a pointed var) + * Q means "Query", response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define SCULLV_IOCSORDER _IOW(SCULLV_IOC_MAGIC, 1, int) +#define SCULLV_IOCTORDER _IO(SCULLV_IOC_MAGIC, 2) +#define SCULLV_IOCGORDER _IOR(SCULLV_IOC_MAGIC, 3, int) +#define SCULLV_IOCQORDER _IO(SCULLV_IOC_MAGIC, 4) +#define SCULLV_IOCXORDER _IOWR(SCULLV_IOC_MAGIC, 5, int) +#define SCULLV_IOCHORDER _IO(SCULLV_IOC_MAGIC, 6) +#define SCULLV_IOCSQSET _IOW(SCULLV_IOC_MAGIC, 7, int) +#define SCULLV_IOCTQSET _IO(SCULLV_IOC_MAGIC, 8) +#define SCULLV_IOCGQSET _IOR(SCULLV_IOC_MAGIC, 9, int) +#define SCULLV_IOCQQSET _IO(SCULLV_IOC_MAGIC, 10) +#define SCULLV_IOCXQSET _IOWR(SCULLV_IOC_MAGIC,11, int) +#define SCULLV_IOCHQSET _IO(SCULLV_IOC_MAGIC, 12) + +#define SCULLV_IOC_MAXNR 12 + + + diff --git a/examples/scullv/scullv_load b/examples/scullv/scullv_load new file mode 100644 index 0000000..9b26276 --- /dev/null +++ b/examples/scullv/scullv_load @@ -0,0 +1,30 @@ +#!/bin/sh +module="scullv" +device="scullv" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# remove stale nodes +rm -f /dev/${device}? + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +ln -sf ${device}0 /dev/${device} + +# give appropriate group/permissions +chgrp $group /dev/${device}[0-3] +chmod $mode /dev/${device}[0-3] diff --git a/examples/scullv/scullv_unload b/examples/scullv/scullv_unload new file mode 100644 index 0000000..13608e8 --- /dev/null +++ b/examples/scullv/scullv_unload @@ -0,0 +1,11 @@ +#!/bin/sh +module="scullv" +device="scullv" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# remove nodes +rm -f /dev/${device}[0-3] /dev/${device} + +exit 0 diff --git a/examples/short/Makefile b/examples/short/Makefile new file mode 100644 index 0000000..1655b3f --- /dev/null +++ b/examples/short/Makefile @@ -0,0 +1,40 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSHORT_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) +CFLAGS += -I.. + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := short.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/short/short.c b/examples/short/short.c new file mode 100644 index 0000000..8bd39ed --- /dev/null +++ b/examples/short/short.c @@ -0,0 +1,692 @@ +/* + * short.c -- Simple Hardware Operations and Raw Tests + * short.c -- also a brief example of interrupt handling ("short int") + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: short.c,v 1.16 2004/10/29 16:45:40 corbet Exp $ + */ + +/* + * FIXME: this driver is not safe with concurrent readers or + * writers. + */ + +#include +#include +#include +#include + +#include +#include /* printk() */ +#include /* everything... */ +#include /* error codes */ +#include /* udelay */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SHORT_NR_PORTS 8 /* use 8 ports by default */ + +/* + * all of the parameters have no "short_" prefix, to save typing when + * specifying them at load time + */ +static int major = 0; /* dynamic by default */ +module_param(major, int, 0); + +static int use_mem = 0; /* default is I/O-mapped */ +module_param(use_mem, int, 0); + +/* default is the first printer port on PC's. "short_base" is there too + because it's what we want to use in the code */ +static unsigned long base = 0x378; +unsigned long short_base = 0; +module_param(base, long, 0); + +/* The interrupt line is undefined by default. "short_irq" is as above */ +static int irq = -1; +volatile int short_irq = -1; +module_param(irq, int, 0); + +static int probe = 0; /* select at load time how to probe irq line */ +module_param(probe, int, 0); + +static int wq = 0; /* select at load time whether a workqueue is used */ +module_param(wq, int, 0); + +static int tasklet = 0; /* select whether a tasklet is used */ +module_param(tasklet, int, 0); + +static int share = 0; /* select at load time whether install a shared irq */ +module_param(share, int, 0); + +MODULE_AUTHOR ("Alessandro Rubini"); +MODULE_LICENSE("Dual BSD/GPL"); + + +unsigned long short_buffer = 0; +unsigned long volatile short_head; +volatile unsigned long short_tail; +DECLARE_WAIT_QUEUE_HEAD(short_queue); + +/* Set up our tasklet if we're doing that. */ +void short_do_tasklet(unsigned long); +DECLARE_TASKLET(short_tasklet, short_do_tasklet, 0); + +/* + * Atomicly increment an index into short_buffer + */ +static inline void short_incr_bp(volatile unsigned long *index, int delta) +{ + unsigned long new = *index + delta; + barrier(); /* Don't optimize these two together */ + *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new; +} + + +/* + * The devices with low minor numbers write/read burst of data to/from + * specific I/O ports (by default the parallel ones). + * + * The device with 128 as minor number returns ascii strings telling + * when interrupts have been received. Writing to the device toggles + * 00/FF on the parallel data lines. If there is a loopback wire, this + * generates interrupts. + */ + +int short_open (struct inode *inode, struct file *filp) +{ + extern struct file_operations short_i_fops; + + if (iminor (inode) & 0x80) + filp->f_op = &short_i_fops; /* the interrupt-driven node */ + return 0; +} + + +int short_release (struct inode *inode, struct file *filp) +{ + return 0; +} + + +/* first, the port-oriented device */ + +enum short_modes {SHORT_DEFAULT=0, SHORT_PAUSE, SHORT_STRING, SHORT_MEMORY}; + +ssize_t do_short_read (struct inode *inode, struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval = count, minor = iminor (inode); + unsigned long port = short_base + (minor&0x0f); + void *address = (void *) short_base + (minor&0x0f); + int mode = (minor&0x70) >> 4; + unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr; + + if (!kbuf) + return -ENOMEM; + ptr = kbuf; + + if (use_mem) + mode = SHORT_MEMORY; + + switch(mode) { + case SHORT_STRING: + insb(port, ptr, count); + rmb(); + break; + + case SHORT_DEFAULT: + while (count--) { + *(ptr++) = inb(port); + rmb(); + } + break; + + case SHORT_MEMORY: + while (count--) { + *ptr++ = ioread8(address); + rmb(); + } + break; + case SHORT_PAUSE: + while (count--) { + *(ptr++) = inb_p(port); + rmb(); + } + break; + + default: /* no more modes defined by now */ + retval = -EINVAL; + break; + } + if ((retval > 0) && copy_to_user(buf, kbuf, retval)) + retval = -EFAULT; + kfree(kbuf); + return retval; +} + + +/* + * Version-specific methods for the fops structure. FIXME don't need anymore. + */ +ssize_t short_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + return do_short_read(filp->f_dentry->d_inode, filp, buf, count, f_pos); +} + + + +ssize_t do_short_write (struct inode *inode, struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval = count, minor = iminor(inode); + unsigned long port = short_base + (minor&0x0f); + void *address = (void *) short_base + (minor&0x0f); + int mode = (minor&0x70) >> 4; + unsigned char *kbuf = kmalloc(count, GFP_KERNEL), *ptr; + + if (!kbuf) + return -ENOMEM; + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + ptr = kbuf; + + if (use_mem) + mode = SHORT_MEMORY; + + switch(mode) { + case SHORT_PAUSE: + while (count--) { + outb_p(*(ptr++), port); + wmb(); + } + break; + + case SHORT_STRING: + outsb(port, ptr, count); + wmb(); + break; + + case SHORT_DEFAULT: + while (count--) { + outb(*(ptr++), port); + wmb(); + } + break; + + case SHORT_MEMORY: + while (count--) { + iowrite8(*ptr++, address); + wmb(); + } + break; + + default: /* no more modes defined by now */ + retval = -EINVAL; + break; + } + kfree(kbuf); + return retval; +} + + +ssize_t short_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + return do_short_write(filp->f_dentry->d_inode, filp, buf, count, f_pos); +} + + + + +unsigned int short_poll(struct file *filp, poll_table *wait) +{ + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; +} + + + + + + +struct file_operations short_fops = { + .owner = THIS_MODULE, + .read = short_read, + .write = short_write, + .poll = short_poll, + .open = short_open, + .release = short_release, +}; + +/* then, the interrupt-related device */ + +ssize_t short_i_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + int count0; + DEFINE_WAIT(wait); + + while (short_head == short_tail) { + prepare_to_wait(&short_queue, &wait, TASK_INTERRUPTIBLE); + if (short_head == short_tail) + schedule(); + finish_wait(&short_queue, &wait); + if (signal_pending (current)) /* a signal arrived */ + return -ERESTARTSYS; /* tell the fs layer to handle it */ + } + /* count0 is the number of readable data bytes */ + count0 = short_head - short_tail; + if (count0 < 0) /* wrapped */ + count0 = short_buffer + PAGE_SIZE - short_tail; + if (count0 < count) count = count0; + + if (copy_to_user(buf, (char *)short_tail, count)) + return -EFAULT; + short_incr_bp (&short_tail, count); + return count; +} + +ssize_t short_i_write (struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + int written = 0, odd = *f_pos & 1; + unsigned long port = short_base; /* output to the parallel data latch */ + void *address = (void *) short_base; + + if (use_mem) { + while (written < count) + iowrite8(0xff * ((++written + odd) & 1), address); + } else { + while (written < count) + outb(0xff * ((++written + odd) & 1), port); + } + + *f_pos += count; + return written; +} + + + + +struct file_operations short_i_fops = { + .owner = THIS_MODULE, + .read = short_i_read, + .write = short_i_write, + .open = short_open, + .release = short_release, +}; + +irqreturn_t short_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct timeval tv; + int written; + + do_gettimeofday(&tv); + + /* Write a 16 byte record. Assume PAGE_SIZE is a multiple of 16 */ + written = sprintf((char *)short_head,"%08u.%06u\n", + (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); + BUG_ON(written != 16); + short_incr_bp(&short_head, written); + wake_up_interruptible(&short_queue); /* awake any reading process */ + return IRQ_HANDLED; +} + +/* + * The following two functions are equivalent to the previous one, + * but split in top and bottom half. First, a few needed variables + */ + +#define NR_TIMEVAL 512 /* length of the array of time values */ + +struct timeval tv_data[NR_TIMEVAL]; /* too lazy to allocate it */ +volatile struct timeval *tv_head=tv_data; +volatile struct timeval *tv_tail=tv_data; + +static struct work_struct short_wq; + + +int short_wq_count = 0; + +/* + * Increment a circular buffer pointer in a way that nobody sees + * an intermediate value. + */ +static inline void short_incr_tv(volatile struct timeval **tvp) +{ + if (*tvp == (tv_data + NR_TIMEVAL - 1)) + *tvp = tv_data; /* Wrap */ + else + (*tvp)++; +} + + + +void short_do_tasklet (unsigned long unused) +{ + int savecount = short_wq_count, written; + short_wq_count = 0; /* we have already been removed from the queue */ + /* + * The bottom half reads the tv array, filled by the top half, + * and prints it to the circular text buffer, which is then consumed + * by reading processes + */ + + /* First write the number of interrupts that occurred before this bh */ + written = sprintf((char *)short_head,"bh after %6i\n",savecount); + short_incr_bp(&short_head, written); + + /* + * Then, write the time values. Write exactly 16 bytes at a time, + * so it aligns with PAGE_SIZE + */ + + do { + written = sprintf((char *)short_head,"%08u.%06u\n", + (int)(tv_tail->tv_sec % 100000000), + (int)(tv_tail->tv_usec)); + short_incr_bp(&short_head, written); + short_incr_tv(&tv_tail); + } while (tv_tail != tv_head); + + wake_up_interruptible(&short_queue); /* awake any reading process */ +} + + +irqreturn_t short_wq_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* Grab the current time information. */ + do_gettimeofday((struct timeval *) tv_head); + short_incr_tv(&tv_head); + + /* Queue the bh. Don't worry about multiple enqueueing */ + schedule_work(&short_wq); + + short_wq_count++; /* record that an interrupt arrived */ + return IRQ_HANDLED; +} + + +/* + * Tasklet top half + */ + +irqreturn_t short_tl_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + do_gettimeofday((struct timeval *) tv_head); /* cast to stop 'volatile' warning */ + short_incr_tv(&tv_head); + tasklet_schedule(&short_tasklet); + short_wq_count++; /* record that an interrupt arrived */ + return IRQ_HANDLED; +} + + + + +irqreturn_t short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int value, written; + struct timeval tv; + + /* If it wasn't short, return immediately */ + value = inb(short_base); + if (!(value & 0x80)) + return IRQ_NONE; + + /* clear the interrupting bit */ + outb(value & 0x7F, short_base); + + /* the rest is unchanged */ + + do_gettimeofday(&tv); + written = sprintf((char *)short_head,"%08u.%06u\n", + (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec)); + short_incr_bp(&short_head, written); + wake_up_interruptible(&short_queue); /* awake any reading process */ + return IRQ_HANDLED; +} + +void short_kernelprobe(void) +{ + int count = 0; + do { + unsigned long mask; + + mask = probe_irq_on(); + outb_p(0x10,short_base+2); /* enable reporting */ + outb_p(0x00,short_base); /* clear the bit */ + outb_p(0xFF,short_base); /* set the bit: interrupt! */ + outb_p(0x00,short_base+2); /* disable reporting */ + udelay(5); /* give it some time */ + short_irq = probe_irq_off(mask); + + if (short_irq == 0) { /* none of them? */ + printk(KERN_INFO "short: no irq reported by probe\n"); + short_irq = -1; + } + /* + * if more than one line has been activated, the result is + * negative. We should service the interrupt (no need for lpt port) + * and loop over again. Loop at most five times, then give up + */ + } while (short_irq < 0 && count++ < 5); + if (short_irq < 0) + printk("short: probe failed %i times, giving up\n", count); +} + +irqreturn_t short_probing(int irq, void *dev_id, struct pt_regs *regs) +{ + if (short_irq == 0) short_irq = irq; /* found */ + if (short_irq != irq) short_irq = -irq; /* ambiguous */ + return IRQ_HANDLED; +} + +void short_selfprobe(void) +{ + int trials[] = {3, 5, 7, 9, 0}; + int tried[] = {0, 0, 0, 0, 0}; + int i, count = 0; + + /* + * install the probing handler for all possible lines. Remember + * the result (0 for success, or -EBUSY) in order to only free + * what has been acquired + */ + for (i = 0; trials[i]; i++) + tried[i] = request_irq(trials[i], short_probing, + SA_INTERRUPT, "short probe", NULL); + + do { + short_irq = 0; /* none got, yet */ + outb_p(0x10,short_base+2); /* enable */ + outb_p(0x00,short_base); + outb_p(0xFF,short_base); /* toggle the bit */ + outb_p(0x00,short_base+2); /* disable */ + udelay(5); /* give it some time */ + + /* the value has been set by the handler */ + if (short_irq == 0) { /* none of them? */ + printk(KERN_INFO "short: no irq reported by probe\n"); + } + /* + * If more than one line has been activated, the result is + * negative. We should service the interrupt (but the lpt port + * doesn't need it) and loop over again. Do it at most 5 times + */ + } while (short_irq <=0 && count++ < 5); + + /* end of loop, uninstall the handler */ + for (i = 0; trials[i]; i++) + if (tried[i] == 0) + free_irq(trials[i], NULL); + + if (short_irq < 0) + printk("short: probe failed %i times, giving up\n", count); +} + + + +/* Finally, init and cleanup */ + +int short_init(void) +{ + int result; + + /* + * first, sort out the base/short_base ambiguity: we'd better + * use short_base in the code, for clarity, but allow setting + * just "base" at load time. Same for "irq". + */ + short_base = base; + short_irq = irq; + + /* Get our needed resources. */ + if (!use_mem) { + if (! request_region(short_base, SHORT_NR_PORTS, "short")) { + printk(KERN_INFO "short: can't get I/O port address 0x%lx\n", + short_base); + return -ENODEV; + } + + } else { + if (! request_mem_region(short_base, SHORT_NR_PORTS, "short")) { + printk(KERN_INFO "short: can't get I/O mem address 0x%lx\n", + short_base); + return -ENODEV; + } + + /* also, ioremap it */ + short_base = (unsigned long) ioremap(short_base, SHORT_NR_PORTS); + /* Hmm... we should check the return value */ + } + /* Here we register our device - should not fail thereafter */ + result = register_chrdev(major, "short", &short_fops); + if (result < 0) { + printk(KERN_INFO "short: can't get major number\n"); + release_region(short_base,SHORT_NR_PORTS); /* FIXME - use-mem case? */ + return result; + } + if (major == 0) major = result; /* dynamic */ + + short_buffer = __get_free_pages(GFP_KERNEL,0); /* never fails */ /* FIXME */ + short_head = short_tail = short_buffer; + + /* + * Fill the workqueue structure, used for the bottom half handler. + * The cast is there to prevent warnings about the type of the + * (unused) argument. + */ + /* this line is in short_init() */ + INIT_WORK(&short_wq, (void (*)(void *)) short_do_tasklet, NULL); + + /* + * Now we deal with the interrupt: either kernel-based + * autodetection, DIY detection or default number + */ + + if (short_irq < 0 && probe == 1) + short_kernelprobe(); + + if (short_irq < 0 && probe == 2) + short_selfprobe(); + + if (short_irq < 0) /* not yet specified: force the default on */ + switch(short_base) { + case 0x378: short_irq = 7; break; + case 0x278: short_irq = 2; break; + case 0x3bc: short_irq = 5; break; + } + + /* + * If shared has been specified, installed the shared handler + * instead of the normal one. Do it first, before a -EBUSY will + * force short_irq to -1. + */ + if (short_irq >= 0 && share > 0) { + result = request_irq(short_irq, short_sh_interrupt, + SA_SHIRQ | SA_INTERRUPT,"short", + short_sh_interrupt); + if (result) { + printk(KERN_INFO "short: can't get assigned irq %i\n", short_irq); + short_irq = -1; + } + else { /* actually enable it -- assume this *is* a parallel port */ + outb(0x10, short_base+2); + } + return 0; /* the rest of the function only installs handlers */ + } + + if (short_irq >= 0) { + result = request_irq(short_irq, short_interrupt, + SA_INTERRUPT, "short", NULL); + if (result) { + printk(KERN_INFO "short: can't get assigned irq %i\n", + short_irq); + short_irq = -1; + } + else { /* actually enable it -- assume this *is* a parallel port */ + outb(0x10,short_base+2); + } + } + + /* + * Ok, now change the interrupt handler if using top/bottom halves + * has been requested + */ + if (short_irq >= 0 && (wq + tasklet) > 0) { + free_irq(short_irq,NULL); + result = request_irq(short_irq, + tasklet ? short_tl_interrupt : + short_wq_interrupt, + SA_INTERRUPT,"short-bh", NULL); + if (result) { + printk(KERN_INFO "short-bh: can't get assigned irq %i\n", + short_irq); + short_irq = -1; + } + } + + return 0; +} + +void short_cleanup(void) +{ + if (short_irq >= 0) { + outb(0x0, short_base + 2); /* disable the interrupt */ + if (!share) free_irq(short_irq, NULL); + else free_irq(short_irq, short_sh_interrupt); + } + /* Make sure we don't leave work queue/tasklet functions running */ + if (tasklet) + tasklet_disable(&short_tasklet); + else + flush_scheduled_work(); + unregister_chrdev(major, "short"); + if (use_mem) { + iounmap((void __iomem *)short_base); + release_mem_region(short_base, SHORT_NR_PORTS); + } else { + release_region(short_base,SHORT_NR_PORTS); + } + if (short_buffer) free_page(short_buffer); +} + +module_init(short_init); +module_exit(short_cleanup); diff --git a/examples/short/short_load b/examples/short/short_load new file mode 100644 index 0000000..407846c --- /dev/null +++ b/examples/short/short_load @@ -0,0 +1,61 @@ +#!/bin/sh +module="short" +device="short" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +# Create 8 entry points, as SHORT_NR_PORTS is 8 by default +rm -f /dev/${device}[0-7] +mknod /dev/${device}0 c $major 0 +mknod /dev/${device}1 c $major 1 +mknod /dev/${device}2 c $major 2 +mknod /dev/${device}3 c $major 3 +mknod /dev/${device}4 c $major 4 +mknod /dev/${device}5 c $major 5 +mknod /dev/${device}6 c $major 6 +mknod /dev/${device}7 c $major 7 + +rm -f /dev/${device}[0-3][ps] +mknod /dev/${device}0p c $major 16 +mknod /dev/${device}1p c $major 17 +mknod /dev/${device}2p c $major 18 +mknod /dev/${device}3p c $major 19 +mknod /dev/${device}4p c $major 20 +mknod /dev/${device}5p c $major 21 +mknod /dev/${device}6p c $major 22 +mknod /dev/${device}7p c $major 23 + +mknod /dev/${device}0s c $major 32 +mknod /dev/${device}1s c $major 33 +mknod /dev/${device}2s c $major 34 +mknod /dev/${device}3s c $major 35 +mknod /dev/${device}4s c $major 36 +mknod /dev/${device}5s c $major 37 +mknod /dev/${device}6s c $major 38 +mknod /dev/${device}7s c $major 39 + +rm -f /dev/${device}int /dev/${device}print +mknod /dev/${device}int c $major 128 +mknod /dev/${device}print c $major 129 + +chgrp $group /dev/${device}[0-7] /dev/${device}[0-7][ps] /dev/${device}int +chmod $mode /dev/${device}[0-7] /dev/${device}[0-7][ps] /dev/${device}int + + + + + + diff --git a/examples/short/short_unload b/examples/short/short_unload new file mode 100644 index 0000000..7cf64dc --- /dev/null +++ b/examples/short/short_unload @@ -0,0 +1,16 @@ +#!/bin/sh +module="short" +device="short" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes + +rm -f /dev/${device}[0-7] /dev/${device}[0-7][ps] \ + /dev/${device}int /dev/${device}print + + + + + diff --git a/examples/shortprint/Makefile b/examples/shortprint/Makefile new file mode 100644 index 0000000..42a9b53 --- /dev/null +++ b/examples/shortprint/Makefile @@ -0,0 +1,31 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + +CFLAGS += -O2 -I.. + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := shortprint.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/shortprint/shortprint.c b/examples/shortprint/shortprint.c new file mode 100644 index 0000000..d7c8520 --- /dev/null +++ b/examples/shortprint/shortprint.c @@ -0,0 +1,521 @@ +/* + * A version of the "short" driver which drives a parallel printer directly, + * with a lot of simplifying assumptions. + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: shortprint.c,v 1.4 2004/09/26 08:01:04 gregkh Exp $ + */ +#include +#include +#include + +#include +#include /* printk() */ +#include /* everything... */ +#include /* error codes */ +#include /* udelay */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "shortprint.h" + +#define SHORTP_NR_PORTS 3 + +/* + * all of the parameters have no "shortp_" prefix, to save typing when + * specifying them at load time + */ +static int major = 0; /* dynamic by default */ +module_param(major, int, 0); + +/* default is the first printer port on PC's. "shortp_base" is there too + because it's what we want to use in the code */ +static unsigned long base = 0x378; +unsigned long shortp_base = 0; +module_param(base, long, 0); + +/* The interrupt line is undefined by default. "shortp_irq" is as above */ +static int irq = -1; +static int shortp_irq = -1; +module_param(irq, int, 0); + +/* Microsecond delay around strobe. */ +static int delay = 0; +static int shortp_delay; +module_param(delay, int, 0); + +MODULE_AUTHOR ("Jonathan Corbet"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * Forwards. + */ +static void shortp_cleanup(void); +static void shortp_timeout(unsigned long unused); + +/* + * Input is managed through a simple circular buffer which, among other things, + * is allowed to overrun if the reader isn't fast enough. That makes life simple + * on the "read" interrupt side, where we don't want to block. + */ +static unsigned long shortp_in_buffer = 0; +static unsigned long volatile shortp_in_head; +static volatile unsigned long shortp_in_tail; +DECLARE_WAIT_QUEUE_HEAD(shortp_in_queue); +static struct timeval shortp_tv; /* When the interrupt happened. */ + +/* + * Atomicly increment an index into shortp_in_buffer + */ +static inline void shortp_incr_bp(volatile unsigned long *index, int delta) +{ + unsigned long new = *index + delta; + barrier (); /* Don't optimize these two together */ + *index = (new >= (shortp_in_buffer + PAGE_SIZE)) ? shortp_in_buffer : new; +} + + +/* + * On the write side we have to be more careful, since we don't want to drop + * data. The semaphore is used to serialize write-side access to the buffer; + * there is only one consumer, so read-side access is unregulated. The + * wait queue will be awakened when space becomes available in the buffer. + */ +static unsigned char *shortp_out_buffer = NULL; +static volatile unsigned char *shortp_out_head, *shortp_out_tail; +static struct semaphore shortp_out_sem; +static DECLARE_WAIT_QUEUE_HEAD(shortp_out_queue); + +/* + * Feeding the output queue to the device is handled by way of a + * workqueue. + */ +static void shortp_do_work(void *); +static DECLARE_WORK(shortp_work, shortp_do_work, NULL); +static struct workqueue_struct *shortp_workqueue; + +/* + * Available space in the output buffer; should be called with the semaphore + * held. Returns contiguous space, so caller need not worry about wraps. + */ +static inline int shortp_out_space(void) +{ + if (shortp_out_head >= shortp_out_tail) { + int space = PAGE_SIZE - (shortp_out_head - shortp_out_buffer); + return (shortp_out_tail == shortp_out_buffer) ? space - 1 : space; + } else + return (shortp_out_tail - shortp_out_head) - 1; +} + +static inline void shortp_incr_out_bp(volatile unsigned char **bp, int incr) +{ + unsigned char *new = (unsigned char *) *bp + incr; + if (new >= (shortp_out_buffer + PAGE_SIZE)) + new -= PAGE_SIZE; + *bp = new; +} + +/* + * The output "process" is controlled by a spin lock; decisions on + * shortp_output_active or manipulation of shortp_out_tail require + * that this lock be held. + */ +static spinlock_t shortp_out_lock; +volatile static int shortp_output_active; +DECLARE_WAIT_QUEUE_HEAD(shortp_empty_queue); /* waked when queue empties */ + +/* + * When output is active, the timer is too, in case we miss interrupts. Hold + * shortp_out_lock if you mess with the timer. + */ +static struct timer_list shortp_timer; +#define TIMEOUT 5*HZ /* Wait a long time */ + + +/* + * Open the device. + */ +static int shortp_open(struct inode *inode, struct file *filp) +{ + return 0; +} + + +static int shortp_release(struct inode *inode, struct file *filp) +{ + /* Wait for any pending output to complete */ + wait_event_interruptible(shortp_empty_queue, shortp_output_active==0); + + return 0; +} + + + +static unsigned int shortp_poll(struct file *filp, poll_table *wait) +{ + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; +} + + + +/* + * The read routine, which doesn't return data from the device; instead, it + * returns timing information just like the "short" device. + */ +static ssize_t shortp_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + int count0; + DEFINE_WAIT(wait); + + while (shortp_in_head == shortp_in_tail) { + prepare_to_wait(&shortp_in_queue, &wait, TASK_INTERRUPTIBLE); + if (shortp_in_head == shortp_in_tail) + schedule(); + finish_wait(&shortp_in_queue, &wait); + if (signal_pending (current)) /* a signal arrived */ + return -ERESTARTSYS; /* tell the fs layer to handle it */ + } + + /* count0 is the number of readable data bytes */ + count0 = shortp_in_head - shortp_in_tail; + if (count0 < 0) /* wrapped */ + count0 = shortp_in_buffer + PAGE_SIZE - shortp_in_tail; + if (count0 < count) + count = count0; + + if (copy_to_user(buf, (char *)shortp_in_tail, count)) + return -EFAULT; + shortp_incr_bp(&shortp_in_tail, count); + return count; +} + + +/* + * Wait for the printer to be ready; this can sleep. + */ +static void shortp_wait(void) +{ + if ((inb(shortp_base + SP_STATUS) & SP_SR_BUSY) == 0) { + printk(KERN_INFO "shortprint: waiting for printer busy\n"); + printk(KERN_INFO "Status is 0x%x\n", inb(shortp_base + SP_STATUS)); + while ((inb(shortp_base + SP_STATUS) & SP_SR_BUSY) == 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(10*HZ); + } + } +} + + +/* + * Write the next character from the buffer. There should *be* a next + * character... The spinlock should be held when this routine is called. + */ +static void shortp_do_write(void) +{ + unsigned char cr = inb(shortp_base + SP_CONTROL); + + /* Something happened; reset the timer */ + mod_timer(&shortp_timer, jiffies + TIMEOUT); + + /* Strobe a byte out to the device */ + outb_p(*shortp_out_tail, shortp_base+SP_DATA); + shortp_incr_out_bp(&shortp_out_tail, 1); + if (shortp_delay) + udelay(shortp_delay); + outb_p(cr | SP_CR_STROBE, shortp_base+SP_CONTROL); + if (shortp_delay) + udelay(shortp_delay); + outb_p(cr & ~SP_CR_STROBE, shortp_base+SP_CONTROL); +} + + +/* + * Start output; call under lock. + */ +static void shortp_start_output(void) +{ + if (shortp_output_active) /* Should never happen */ + return; + + /* Set up our 'missed interrupt' timer */ + shortp_output_active = 1; + shortp_timer.expires = jiffies + TIMEOUT; + add_timer(&shortp_timer); + + /* And get the process going. */ + queue_work(shortp_workqueue, &shortp_work); +} + + +/* + * Write to the device. + */ +static ssize_t shortp_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + int space, written = 0; + unsigned long flags; + /* + * Take and hold the semaphore for the entire duration of the operation. The + * consumer side ignores it, and it will keep other data from interleaving + * with ours. + */ + if (down_interruptible(&shortp_out_sem)) + return -ERESTARTSYS; + /* + * Out with the data. + */ + while (written < count) { + /* Hang out until some buffer space is available. */ + space = shortp_out_space(); + if (space <= 0) { + if (wait_event_interruptible(shortp_out_queue, + (space = shortp_out_space()) > 0)) + goto out; + } + + /* Move data into the buffer. */ + if ((space + written) > count) + space = count - written; + if (copy_from_user((char *) shortp_out_head, buf, space)) { + up(&shortp_out_sem); + return -EFAULT; + } + shortp_incr_out_bp(&shortp_out_head, space); + buf += space; + written += space; + + /* If no output is active, make it active. */ + spin_lock_irqsave(&shortp_out_lock, flags); + if (! shortp_output_active) + shortp_start_output(); + spin_unlock_irqrestore(&shortp_out_lock, flags); + } + +out: + *f_pos += written; + up(&shortp_out_sem); + return written; +} + + +/* + * The bottom-half handler. + */ + + +static void shortp_do_work(void *unused) +{ + int written; + unsigned long flags; + + /* Wait until the device is ready */ + shortp_wait(); + + spin_lock_irqsave(&shortp_out_lock, flags); + + /* Have we written everything? */ + if (shortp_out_head == shortp_out_tail) { /* empty */ + shortp_output_active = 0; + wake_up_interruptible(&shortp_empty_queue); + del_timer(&shortp_timer); + } + /* Nope, write another byte */ + else + shortp_do_write(); + + /* If somebody's waiting, maybe wake them up. */ + if (((PAGE_SIZE + shortp_out_tail - shortp_out_head) % PAGE_SIZE) > SP_MIN_SPACE) { + wake_up_interruptible(&shortp_out_queue); + } + spin_unlock_irqrestore(&shortp_out_lock, flags); + + /* Handle the "read" side operation */ + written = sprintf((char *)shortp_in_head, "%08u.%06u\n", + (int)(shortp_tv.tv_sec % 100000000), + (int)(shortp_tv.tv_usec)); + shortp_incr_bp(&shortp_in_head, written); + wake_up_interruptible(&shortp_in_queue); /* awake any reading process */ +} + + +/* + * The top-half interrupt handler. + */ +static irqreturn_t shortp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (! shortp_output_active) + return IRQ_NONE; + + /* Remember the time, and farm off the rest to the workqueue function */ + do_gettimeofday(&shortp_tv); + queue_work(shortp_workqueue, &shortp_work); + return IRQ_HANDLED; +} + +/* + * Interrupt timeouts. Just because we got a timeout doesn't mean that + * things have gone wrong, however; printers can spend an awful long time + * just thinking about things. + */ +static void shortp_timeout(unsigned long unused) +{ + unsigned long flags; + unsigned char status; + + if (! shortp_output_active) + return; + spin_lock_irqsave(&shortp_out_lock, flags); + status = inb(shortp_base + SP_STATUS); + + /* If the printer is still busy we just reset the timer */ + if ((status & SP_SR_BUSY) == 0 || (status & SP_SR_ACK)) { + shortp_timer.expires = jiffies + TIMEOUT; + add_timer(&shortp_timer); + spin_unlock_irqrestore(&shortp_out_lock, flags); + return; + } + + /* Otherwise we must have dropped an interrupt. */ + spin_unlock_irqrestore(&shortp_out_lock, flags); + shortp_interrupt(shortp_irq, NULL, NULL); +} + + + + + +static struct file_operations shortp_fops = { + .read = shortp_read, + .write = shortp_write, + .open = shortp_open, + .release = shortp_release, + .poll = shortp_poll, + .owner = THIS_MODULE +}; + + + + +/* + * Module initialization + */ + +static int shortp_init(void) +{ + int result; + + /* + * first, sort out the base/shortp_base ambiguity: we'd better + * use shortp_base in the code, for clarity, but allow setting + * just "base" at load time. Same for "irq". + */ + shortp_base = base; + shortp_irq = irq; + shortp_delay = delay; + + /* Get our needed resources. */ + if (! request_region(shortp_base, SHORTP_NR_PORTS, "shortprint")) { + printk(KERN_INFO "shortprint: can't get I/O port address 0x%lx\n", + shortp_base); + return -ENODEV; + } + + /* Register the device */ + result = register_chrdev(major, "shortprint", &shortp_fops); + if (result < 0) { + printk(KERN_INFO "shortp: can't get major number\n"); + release_region(shortp_base, SHORTP_NR_PORTS); + return result; + } + if (major == 0) + major = result; /* dynamic */ + + /* Initialize the input buffer. */ + shortp_in_buffer = __get_free_pages(GFP_KERNEL, 0); /* never fails */ + shortp_in_head = shortp_in_tail = shortp_in_buffer; + + /* And the output buffer. */ + shortp_out_buffer = (unsigned char *) __get_free_pages(GFP_KERNEL, 0); + shortp_out_head = shortp_out_tail = shortp_out_buffer; + sema_init(&shortp_out_sem, 1); + + /* And the output info */ + shortp_output_active = 0; + spin_lock_init(&shortp_out_lock); + init_timer(&shortp_timer); + shortp_timer.function = shortp_timeout; + shortp_timer.data = 0; + + /* Set up our workqueue. */ + shortp_workqueue = create_singlethread_workqueue("shortprint"); + + /* If no IRQ was explicitly requested, pick a default */ + if (shortp_irq < 0) + switch(shortp_base) { + case 0x378: shortp_irq = 7; break; + case 0x278: shortp_irq = 2; break; + case 0x3bc: shortp_irq = 5; break; + } + + /* Request the IRQ */ + result = request_irq(shortp_irq, shortp_interrupt, 0, "shortprint", NULL); + if (result) { + printk(KERN_INFO "shortprint: can't get assigned irq %i\n", + shortp_irq); + shortp_irq = -1; + shortp_cleanup (); + return result; + } + + /* Initialize the control register, turning on interrupts. */ + outb(SP_CR_IRQ | SP_CR_SELECT | SP_CR_INIT, shortp_base + SP_CONTROL); + + return 0; +} + +static void shortp_cleanup(void) +{ + /* Return the IRQ if we have one */ + if (shortp_irq >= 0) { + outb(0x0, shortp_base + SP_CONTROL); /* disable the interrupt */ + free_irq(shortp_irq, NULL); + } + + /* All done with the device */ + unregister_chrdev(major, "shortprint"); + release_region(shortp_base,SHORTP_NR_PORTS); + + /* Don't leave any timers floating around. Note that any active output + is effectively stopped by turning off the interrupt */ + if (shortp_output_active) + del_timer_sync (&shortp_timer); + flush_workqueue(shortp_workqueue); + destroy_workqueue(shortp_workqueue); + + if (shortp_in_buffer) + free_page(shortp_in_buffer); + if (shortp_out_buffer) + free_page((unsigned long) shortp_out_buffer); +} + +module_init(shortp_init); +module_exit(shortp_cleanup); diff --git a/examples/shortprint/shortprint.h b/examples/shortprint/shortprint.h new file mode 100644 index 0000000..1531025 --- /dev/null +++ b/examples/shortprint/shortprint.h @@ -0,0 +1,46 @@ +/* + * Useful info describing the parallel port device. + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + */ + +/* + * Register offsets + */ +#define SP_DATA 0x00 +#define SP_STATUS 0x01 +#define SP_CONTROL 0x02 +#define SP_NPORTS 3 + +/* + * Status register bits. + */ +#define SP_SR_BUSY 0x80 +#define SP_SR_ACK 0x40 +#define SP_SR_PAPER 0x20 +#define SP_SR_ONLINE 0x10 +#define SP_SR_ERR 0x08 + +/* + * Control register. + */ +#define SP_CR_IRQ 0x10 +#define SP_CR_SELECT 0x08 +#define SP_CR_INIT 0x04 +#define SP_CR_AUTOLF 0x02 +#define SP_CR_STROBE 0x01 + +/* + * Minimum space before waking up a writer. + */ +#define SP_MIN_SPACE PAGE_SIZE/2 diff --git a/examples/shortprint/shortprint_load b/examples/shortprint/shortprint_load new file mode 100644 index 0000000..4b08609 --- /dev/null +++ b/examples/shortprint/shortprint_load @@ -0,0 +1,31 @@ +#!/bin/sh +module="shortprint" +device="shortprint" +mode="666" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +# Create 8 entry points, as SHORT_NR_PORTS is 8 by default +rm -f /dev/${device} +mknod /dev/${device} c $major 0 + +chgrp $group /dev/${device} +chmod $mode /dev/${device} + + + + + + diff --git a/examples/shortprint/shortprint_unload b/examples/shortprint/shortprint_unload new file mode 100644 index 0000000..88e62a7 --- /dev/null +++ b/examples/shortprint/shortprint_unload @@ -0,0 +1,15 @@ +#!/bin/sh +module="shortprint" +device="shortprint" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes +rm -f /dev/${device} + + + + + + diff --git a/examples/simple/Makefile b/examples/simple/Makefile new file mode 100644 index 0000000..429f743 --- /dev/null +++ b/examples/simple/Makefile @@ -0,0 +1,39 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR) + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := simple.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINCDIR=$(PWD)/../include modules + +endif + + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/simple/simple.c b/examples/simple/simple.c new file mode 100644 index 0000000..e6860c7 --- /dev/null +++ b/examples/simple/simple.c @@ -0,0 +1,235 @@ +/* + * Simple - REALLY simple memory mapping demonstration. + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: simple.c,v 1.12 2005/01/31 16:15:31 rubini Exp $ + */ + +#include +#include +#include +#include + +#include /* printk() */ +#include /* kmalloc() */ +#include /* everything... */ +#include /* error codes */ +#include /* size_t */ +#include +#include +#include +#include + +#include + +static int simple_major = 0; +module_param(simple_major, int, 0); +MODULE_AUTHOR("Jonathan Corbet"); +MODULE_LICENSE("Dual BSD/GPL"); + +/* + * Open the device; in fact, there's nothing to do here. + */ +static int simple_open (struct inode *inode, struct file *filp) +{ + return 0; +} + + +/* + * Closing is just as simpler. + */ +static int simple_release(struct inode *inode, struct file *filp) +{ + return 0; +} + + + +/* + * Common VMA ops. + */ + +void simple_vma_open(struct vm_area_struct *vma) +{ + printk(KERN_NOTICE "Simple VMA open, virt %lx, phys %lx\n", + vma->vm_start, vma->vm_pgoff << PAGE_SHIFT); +} + +void simple_vma_close(struct vm_area_struct *vma) +{ + printk(KERN_NOTICE "Simple VMA close.\n"); +} + + +/* + * The remap_pfn_range version of mmap. This one is heavily borrowed + * from drivers/char/mem.c. + */ + +static struct vm_operations_struct simple_remap_vm_ops = { + .open = simple_vma_open, + .close = simple_vma_close, +}; + +static int simple_remap_mmap(struct file *filp, struct vm_area_struct *vma) +{ + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + vma->vm_ops = &simple_remap_vm_ops; + simple_vma_open(vma); + return 0; +} + + + +/* + * The nopage version. + */ +struct page *simple_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct page *pageptr; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long physaddr = address - vma->vm_start + offset; + unsigned long pageframe = physaddr >> PAGE_SHIFT; + +// Eventually remove these printks + printk (KERN_NOTICE "---- Nopage, off %lx phys %lx\n", offset, physaddr); + printk (KERN_NOTICE "VA is %p\n", __va (physaddr)); + printk (KERN_NOTICE "Page at %p\n", virt_to_page (__va (physaddr))); + if (!pfn_valid(pageframe)) + return NOPAGE_SIGBUS; + pageptr = pfn_to_page(pageframe); + printk (KERN_NOTICE "page->index = %ld mapping %p\n", pageptr->index, pageptr->mapping); + printk (KERN_NOTICE "Page frame %ld\n", pageframe); + get_page(pageptr); + if (type) + *type = VM_FAULT_MINOR; + return pageptr; +} + +static struct vm_operations_struct simple_nopage_vm_ops = { + .open = simple_vma_open, + .close = simple_vma_close, + .nopage = simple_vma_nopage, +}; + +static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC)) + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + vma->vm_ops = &simple_nopage_vm_ops; + simple_vma_open(vma); + return 0; +} + + +/* + * Set up the cdev structure for a device. + */ +static void simple_setup_cdev(struct cdev *dev, int minor, + struct file_operations *fops) +{ + int err, devno = MKDEV(simple_major, minor); + + cdev_init(dev, fops); + dev->owner = THIS_MODULE; + dev->ops = fops; + err = cdev_add (dev, devno, 1); + /* Fail gracefully if need be */ + if (err) + printk (KERN_NOTICE "Error %d adding simple%d", err, minor); +} + + +/* + * Our various sub-devices. + */ +/* Device 0 uses remap_pfn_range */ +static struct file_operations simple_remap_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .release = simple_release, + .mmap = simple_remap_mmap, +}; + +/* Device 1 uses nopage */ +static struct file_operations simple_nopage_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .release = simple_release, + .mmap = simple_nopage_mmap, +}; + +#define MAX_SIMPLE_DEV 2 + +#if 0 +static struct file_operations *simple_fops[MAX_SIMPLE_DEV] = { + &simple_remap_ops, + &simple_nopage_ops, +}; +#endif + +/* + * We export two simple devices. There's no need for us to maintain any + * special housekeeping info, so we just deal with raw cdevs. + */ +static struct cdev SimpleDevs[MAX_SIMPLE_DEV]; + +/* + * Module housekeeping. + */ +static int simple_init(void) +{ + int result; + dev_t dev = MKDEV(simple_major, 0); + + /* Figure out our device number. */ + if (simple_major) + result = register_chrdev_region(dev, 2, "simple"); + else { + result = alloc_chrdev_region(&dev, 0, 2, "simple"); + simple_major = MAJOR(dev); + } + if (result < 0) { + printk(KERN_WARNING "simple: unable to get major %d\n", simple_major); + return result; + } + if (simple_major == 0) + simple_major = result; + + /* Now set up two cdevs. */ + simple_setup_cdev(SimpleDevs, 0, &simple_remap_ops); + simple_setup_cdev(SimpleDevs + 1, 1, &simple_nopage_ops); + return 0; +} + + +static void simple_cleanup(void) +{ + cdev_del(SimpleDevs); + cdev_del(SimpleDevs + 1); + unregister_chrdev_region(MKDEV(simple_major, 0), 2); +} + + +module_init(simple_init); +module_exit(simple_cleanup); diff --git a/examples/simple/simple_load b/examples/simple/simple_load new file mode 100644 index 0000000..5cd004d --- /dev/null +++ b/examples/simple/simple_load @@ -0,0 +1,26 @@ +#!/bin/sh +module="simple" +device="simple" +mode="664" + +# Group: since distributions do it differently, look for wheel or use staff +if grep '^staff:' /etc/group > /dev/null; then + group="staff" +else + group="wheel" +fi + +# invoke insmod with all arguments we got +# and use a pathname, as newer modutils don't look in . by default +/sbin/insmod -f ./$module.ko $* || exit 1 + +major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` + +# Remove stale nodes and replace them, then give gid and perms +# Usually the script is shorter, it's simple that has several devices in it. + +rm -f /dev/${device}[rn] +mknod /dev/${device}r c $major 0 +mknod /dev/${device}n c $major 1 +chgrp $group /dev/${device}[rn] +chmod $mode /dev/${device}[rn] diff --git a/examples/simple/simple_unload b/examples/simple/simple_unload new file mode 100644 index 0000000..dca9421 --- /dev/null +++ b/examples/simple/simple_unload @@ -0,0 +1,14 @@ +#!/bin/sh +module="simple" +device="simple" + +# invoke rmmod with all arguments we got +/sbin/rmmod $module $* || exit 1 + +# Remove stale nodes +rm -f /dev/${device}[rn] + + + + + diff --git a/examples/skull/Makefile b/examples/skull/Makefile new file mode 100644 index 0000000..0152a79 --- /dev/null +++ b/examples/skull/Makefile @@ -0,0 +1 @@ +foo: diff --git a/examples/skull/skull_clean.c b/examples/skull/skull_clean.c new file mode 100644 index 0000000..9d1e789 --- /dev/null +++ b/examples/skull/skull_clean.c @@ -0,0 +1,22 @@ +#include +#include +#include + +#include + +void skull_release(unsigned int port, unsigned int range) +{ + release_region(port,range); +} + +void skull_cleanup(void) +{ + /* should put real values here ... */ + /* skull_release(0,0); */ +} + +module_exit(skull_cleanup); + + + + diff --git a/examples/skull/skull_init.c b/examples/skull/skull_init.c new file mode 100644 index 0000000..aaa06e2 --- /dev/null +++ b/examples/skull/skull_init.c @@ -0,0 +1,200 @@ +/* + * skull.c -- sample typeless module. + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * BUGS: + * -it only runs on intel platforms. + * -readb() should be used (see short.c): skull doesn't work with 2.1 + * + */ + +/* jc: cleaned up, but not yet run for anything */ + +#include +#include +#include +#include + +#include /* printk */ +#include +#include +#include /* cli(), *_flags */ +#include /* vremap (2.0) */ +#include /* ioremap */ + +/* The region we look at. */ +#define ISA_REGION_BEGIN 0xA0000 +#define ISA_REGION_END 0x100000 +#define STEP 2048 + +/* have three symbols to export */ + void skull_fn1(void){} +static void skull_fn2(void){} + int skull_variable; + +EXPORT_SYMBOL (skull_fn1); +EXPORT_SYMBOL (skull_fn2); +EXPORT_SYMBOL (skull_variable); + + +/* perform hardware autodetection */ +int skull_probe_hw(unsigned int port, unsigned int range) +{ + /* do smart probing here */ + return -1; /* not found :-) */ +} + +/* perform hardware initalizazion */ +int skull_init_board(unsigned int port) +{ + /* do smart initalization here */ + return 0; /* done :-) */ +} + +/* detect the the device if the region is still free */ +static int skull_detect(unsigned int port, unsigned int range) +{ + int err; + + if ((err = check_region(port,range)) < 0) return err; /* busy */ + if (skull_probe_hw(port,range) != 0) return -ENODEV; /* not found */ + request_region(port,range,"skull"); /* "Can't fail" */ + return 0; +} + +/* + * port ranges: the device can reside between + * 0x280 and 0x300, in step of 0x10. It uses 0x10 ports. + */ +#define SKULL_PORT_FLOOR 0x280 +#define SKULL_PORT_CEIL 0x300 +#define SKULL_PORT_RANGE 0x010 + +/* + * the following function performs autodetection, unless a specific + * value was assigned by insmod to "skull_port_base" + */ + +static int skull_port_base=0; /* 0 forces autodetection */ +module_param(skull_port_base, int, 0); + +static int skull_find_hw(void) /* returns the # of devices */ +{ + /* base is either the load-time value or the first trial */ + int base = skull_port_base ? skull_port_base + : SKULL_PORT_FLOOR; + int result = 0; + + /* loop one time if value assigned, try them all if autodetecting */ + do { + if (skull_detect(base, SKULL_PORT_RANGE) == 0) { + skull_init_board(base); + result++; + } + base += SKULL_PORT_RANGE; /* prepare for next trial */ + } + while (skull_port_base == 0 && base < SKULL_PORT_CEIL); + + return result; +} + + +int skull_init(void) +{ + /* + * Print the isa region map, in blocks of 2K bytes. + * This is not the best code, as it prints too many lines, + * but it deserves to remain short to be included in the book. + * Note also that read() should be used instead of pointers. + */ + unsigned char oldval, newval; /* values read from memory */ + unsigned long flags; /* used to hold system flags */ + unsigned long add, i; + void *base; + + /* Use ioremap to get a handle on our region */ + base = ioremap(ISA_REGION_BEGIN, ISA_REGION_END - ISA_REGION_BEGIN); + base -= ISA_REGION_BEGIN; /* Do the offset once */ + + /* probe all the memory hole in 2KB steps */ + for (add = ISA_REGION_BEGIN; add < ISA_REGION_END; add += STEP) { + /* + * Check for an already allocated region. + */ + if (check_mem_region (add, 2048)) { + printk(KERN_INFO "%lx: Allocated\n", add); + continue; + } + /* + * Read and write the beginning of the region and see what happens. + */ + save_flags(flags); + cli(); + oldval = readb (base + add); /* Read a byte */ + writeb (oldval^0xff, base + add); + mb(); + newval = readb (base + add); + writeb (oldval, base + add); + restore_flags(flags); + + if ((oldval^newval) == 0xff) { /* we re-read our change: it's ram */ + printk(KERN_INFO "%lx: RAM\n", add); + continue; + } + if ((oldval^newval) != 0) { /* random bits changed: it's empty */ + printk(KERN_INFO "%lx: empty\n", add); + continue; + } + + /* + * Expansion rom (executed at boot time by the bios) + * has a signature where the first byt is 0x55, the second 0xaa, + * and the third byte indicates the size of such rom + */ + if ( (oldval == 0x55) && (readb (base + add + 1) == 0xaa)) { + int size = 512 * readb (base + add + 2); + printk(KERN_INFO "%lx: Expansion ROM, %i bytes\n", + add, size); + add += (size & ~2048) - 2048; /* skip it */ + continue; + } + + /* + * If the tests above failed, we still don't know if it is ROM or + * empty. Since empty memory can appear as 0x00, 0xff, or the low + * address byte, we must probe multiple bytes: if at least one of + * them is different from these three values, then this is rom + * (though not boot rom). + */ + printk(KERN_INFO "%lx: ", add); + for (i=0; i<5; i++) { + unsigned long radd = add + 57*(i+1); /* a "random" value */ + unsigned char val = readb (base + radd); + if (val && val != 0xFF && val != ((unsigned long) radd&0xFF)) + break; + } + printk("%s\n", i==5 ? "empty" : "ROM"); + } + + /* + * Find you hardware + */ + skull_find_hw(); + + /* + * Always fail to load (or suceed). + */ + return 0; +} + +module_init(skull_init); diff --git a/examples/snull/Makefile b/examples/snull/Makefile new file mode 100644 index 0000000..17949c1 --- /dev/null +++ b/examples/snull/Makefile @@ -0,0 +1,41 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) +CFLAGS += -I.. + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := snull.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/snull/snull.c b/examples/snull/snull.c new file mode 100644 index 0000000..6fbde81 --- /dev/null +++ b/examples/snull/snull.c @@ -0,0 +1,736 @@ +/* + * snull.c -- the Simple Network Utility + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + * + * $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $ + */ + +#include +#include +#include +#include + +#include +#include /* printk() */ +#include /* kmalloc() */ +#include /* error codes */ +#include /* size_t */ +#include /* mark_bh */ + +#include +#include /* struct device, and other headers */ +#include /* eth_type_trans */ +#include /* struct iphdr */ +#include /* struct tcphdr */ +#include + +#include "snull.h" + +#include +#include + +MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet"); +MODULE_LICENSE("Dual BSD/GPL"); + + +/* + * Transmitter lockup simulation, normally disabled. + */ +static int lockup = 0; +module_param(lockup, int, 0); + +static int timeout = SNULL_TIMEOUT; +module_param(timeout, int, 0); + +/* + * Do we run in NAPI mode? + */ +static int use_napi = 0; +module_param(use_napi, int, 0); + + +/* + * A structure representing an in-flight packet. + */ +struct snull_packet { + struct snull_packet *next; + struct net_device *dev; + int datalen; + u8 data[ETH_DATA_LEN]; +}; + +int pool_size = 8; +module_param(pool_size, int, 0); + +/* + * This structure is private to each device. It is used to pass + * packets in and out, so there is place for a packet + */ + +struct snull_priv { + struct net_device_stats stats; + int status; + struct snull_packet *ppool; + struct snull_packet *rx_queue; /* List of incoming packets */ + int rx_int_enabled; + int tx_packetlen; + u8 *tx_packetdata; + struct sk_buff *skb; + spinlock_t lock; +}; + +static void snull_tx_timeout(struct net_device *dev); +static void (*snull_interrupt)(int, void *, struct pt_regs *); + +/* + * Set up a device's packet pool. + */ +void snull_setup_pool(struct net_device *dev) +{ + struct snull_priv *priv = netdev_priv(dev); + int i; + struct snull_packet *pkt; + + priv->ppool = NULL; + for (i = 0; i < pool_size; i++) { + pkt = kmalloc (sizeof (struct snull_packet), GFP_KERNEL); + if (pkt == NULL) { + printk (KERN_NOTICE "Ran out of memory allocating packet pool\n"); + return; + } + pkt->dev = dev; + pkt->next = priv->ppool; + priv->ppool = pkt; + } +} + +void snull_teardown_pool(struct net_device *dev) +{ + struct snull_priv *priv = netdev_priv(dev); + struct snull_packet *pkt; + + while ((pkt = priv->ppool)) { + priv->ppool = pkt->next; + kfree (pkt); + /* FIXME - in-flight packets ? */ + } +} + +/* + * Buffer/pool management. + */ +struct snull_packet *snull_get_tx_buffer(struct net_device *dev) +{ + struct snull_priv *priv = netdev_priv(dev); + unsigned long flags; + struct snull_packet *pkt; + + spin_lock_irqsave(&priv->lock, flags); + pkt = priv->ppool; + priv->ppool = pkt->next; + if (priv->ppool == NULL) { + printk (KERN_INFO "Pool empty\n"); + netif_stop_queue(dev); + } + spin_unlock_irqrestore(&priv->lock, flags); + return pkt; +} + + +void snull_release_buffer(struct snull_packet *pkt) +{ + unsigned long flags; + struct snull_priv *priv = netdev_priv(pkt->dev); + + spin_lock_irqsave(&priv->lock, flags); + pkt->next = priv->ppool; + priv->ppool = pkt; + spin_unlock_irqrestore(&priv->lock, flags); + if (netif_queue_stopped(pkt->dev) && pkt->next == NULL) + netif_wake_queue(pkt->dev); +} + +void snull_enqueue_buf(struct net_device *dev, struct snull_packet *pkt) +{ + unsigned long flags; + struct snull_priv *priv = netdev_priv(dev); + + spin_lock_irqsave(&priv->lock, flags); + pkt->next = priv->rx_queue; /* FIXME - misorders packets */ + priv->rx_queue = pkt; + spin_unlock_irqrestore(&priv->lock, flags); +} + +struct snull_packet *snull_dequeue_buf(struct net_device *dev) +{ + struct snull_priv *priv = netdev_priv(dev); + struct snull_packet *pkt; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pkt = priv->rx_queue; + if (pkt != NULL) + priv->rx_queue = pkt->next; + spin_unlock_irqrestore(&priv->lock, flags); + return pkt; +} + +/* + * Enable and disable receive interrupts. + */ +static void snull_rx_ints(struct net_device *dev, int enable) +{ + struct snull_priv *priv = netdev_priv(dev); + priv->rx_int_enabled = enable; +} + + +/* + * Open and close + */ + +int snull_open(struct net_device *dev) +{ + /* request_region(), request_irq(), .... (like fops->open) */ + + /* + * Assign the hardware address of the board: use "\0SNULx", where + * x is 0 or 1. The first byte is '\0' to avoid being a multicast + * address (the first byte of multicast addrs is odd). + */ + memcpy(dev->dev_addr, "\0SNUL0", ETH_ALEN); + if (dev == snull_devs[1]) + dev->dev_addr[ETH_ALEN-1]++; /* \0SNUL1 */ + netif_start_queue(dev); + return 0; +} + +int snull_release(struct net_device *dev) +{ + /* release ports, irq and such -- like fops->close */ + + netif_stop_queue(dev); /* can't transmit any more */ + return 0; +} + +/* + * Configuration changes (passed on by ifconfig) + */ +int snull_config(struct net_device *dev, struct ifmap *map) +{ + if (dev->flags & IFF_UP) /* can't act on a running interface */ + return -EBUSY; + + /* Don't allow changing the I/O address */ + if (map->base_addr != dev->base_addr) { + printk(KERN_WARNING "snull: Can't change I/O address\n"); + return -EOPNOTSUPP; + } + + /* Allow changing the IRQ */ + if (map->irq != dev->irq) { + dev->irq = map->irq; + /* request_irq() is delayed to open-time */ + } + + /* ignore other fields */ + return 0; +} + +/* + * Receive a packet: retrieve, encapsulate and pass over to upper levels + */ +void snull_rx(struct net_device *dev, struct snull_packet *pkt) +{ + struct sk_buff *skb; + struct snull_priv *priv = netdev_priv(dev); + + /* + * The packet has been retrieved from the transmission + * medium. Build an skb around it, so upper layers can handle it + */ + skb = dev_alloc_skb(pkt->datalen + 2); + if (!skb) { + if (printk_ratelimit()) + printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n"); + priv->stats.rx_dropped++; + goto out; + } + skb_reserve(skb, 2); /* align IP on 16B boundary */ + memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen); + + /* Write metadata, and then pass to the receive level */ + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + priv->stats.rx_packets++; + priv->stats.rx_bytes += pkt->datalen; + netif_rx(skb); + out: + return; +} + + +/* + * The poll implementation. + */ +static int snull_poll(struct net_device *dev, int *budget) +{ + int npackets = 0, quota = min(dev->quota, *budget); + struct sk_buff *skb; + struct snull_priv *priv = netdev_priv(dev); + struct snull_packet *pkt; + + while (npackets < quota && priv->rx_queue) { + pkt = snull_dequeue_buf(dev); + skb = dev_alloc_skb(pkt->datalen + 2); + if (! skb) { + if (printk_ratelimit()) + printk(KERN_NOTICE "snull: packet dropped\n"); + priv->stats.rx_dropped++; + snull_release_buffer(pkt); + continue; + } + skb_reserve(skb, 2); /* align IP on 16B boundary */ + memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen); + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + netif_receive_skb(skb); + + /* Maintain stats */ + npackets++; + priv->stats.rx_packets++; + priv->stats.rx_bytes += pkt->datalen; + snull_release_buffer(pkt); + } + /* If we processed all packets, we're done; tell the kernel and reenable ints */ + *budget -= npackets; + dev->quota -= npackets; + if (! priv->rx_queue) { + netif_rx_complete(dev); + snull_rx_ints(dev, 1); + return 0; + } + /* We couldn't process everything. */ + return 1; +} + + +/* + * The typical interrupt entry point + */ +static void snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int statusword; + struct snull_priv *priv; + struct snull_packet *pkt = NULL; + /* + * As usual, check the "device" pointer to be sure it is + * really interrupting. + * Then assign "struct device *dev" + */ + struct net_device *dev = (struct net_device *)dev_id; + /* ... and check with hw if it's really ours */ + + /* paranoid */ + if (!dev) + return; + + /* Lock the device */ + priv = netdev_priv(dev); + spin_lock(&priv->lock); + + /* retrieve statusword: real netdevices use I/O instructions */ + statusword = priv->status; + priv->status = 0; + if (statusword & SNULL_RX_INTR) { + /* send it to snull_rx for handling */ + pkt = priv->rx_queue; + if (pkt) { + priv->rx_queue = pkt->next; + snull_rx(dev, pkt); + } + } + if (statusword & SNULL_TX_INTR) { + /* a transmission is over: free the skb */ + priv->stats.tx_packets++; + priv->stats.tx_bytes += priv->tx_packetlen; + dev_kfree_skb(priv->skb); + } + + /* Unlock the device and we are done */ + spin_unlock(&priv->lock); + if (pkt) snull_release_buffer(pkt); /* Do this outside the lock! */ + return; +} + +/* + * A NAPI interrupt handler. + */ +static void snull_napi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int statusword; + struct snull_priv *priv; + + /* + * As usual, check the "device" pointer for shared handlers. + * Then assign "struct device *dev" + */ + struct net_device *dev = (struct net_device *)dev_id; + /* ... and check with hw if it's really ours */ + + /* paranoid */ + if (!dev) + return; + + /* Lock the device */ + priv = netdev_priv(dev); + spin_lock(&priv->lock); + + /* retrieve statusword: real netdevices use I/O instructions */ + statusword = priv->status; + priv->status = 0; + if (statusword & SNULL_RX_INTR) { + snull_rx_ints(dev, 0); /* Disable further interrupts */ + netif_rx_schedule(dev); + } + if (statusword & SNULL_TX_INTR) { + /* a transmission is over: free the skb */ + priv->stats.tx_packets++; + priv->stats.tx_bytes += priv->tx_packetlen; + dev_kfree_skb(priv->skb); + } + + /* Unlock the device and we are done */ + spin_unlock(&priv->lock); + return; +} + + + +/* + * Transmit a packet (low level interface) + */ +static void snull_hw_tx(char *buf, int len, struct net_device *dev) +{ + /* + * This function deals with hw details. This interface loops + * back the packet to the other snull interface (if any). + * In other words, this function implements the snull behaviour, + * while all other procedures are rather device-independent + */ + struct iphdr *ih; + struct net_device *dest; + struct snull_priv *priv; + u32 *saddr, *daddr; + struct snull_packet *tx_buffer; + + /* I am paranoid. Ain't I? */ + if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) { + printk("snull: Hmm... packet too short (%i octets)\n", + len); + return; + } + + if (0) { /* enable this conditional to look at the data */ + int i; + PDEBUG("len is %i\n" KERN_DEBUG "data:",len); + for (i=14 ; isaddr; + daddr = &ih->daddr; + + ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */ + ((u8 *)daddr)[2] ^= 1; + + ih->check = 0; /* and rebuild the checksum (ip needs it) */ + ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl); + + if (dev == snull_devs[0]) + PDEBUGG("%08x:%05i --> %08x:%05i\n", + ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source), + ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest)); + else + PDEBUGG("%08x:%05i <-- %08x:%05i\n", + ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest), + ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source)); + + /* + * Ok, now the packet is ready for transmission: first simulate a + * receive interrupt on the twin device, then a + * transmission-done on the transmitting device + */ + dest = snull_devs[dev == snull_devs[0] ? 1 : 0]; + priv = netdev_priv(dest); + tx_buffer = snull_get_tx_buffer(dev); + tx_buffer->datalen = len; + memcpy(tx_buffer->data, buf, len); + snull_enqueue_buf(dest, tx_buffer); + if (priv->rx_int_enabled) { + priv->status |= SNULL_RX_INTR; + snull_interrupt(0, dest, NULL); + } + + priv = netdev_priv(dev); + priv->tx_packetlen = len; + priv->tx_packetdata = buf; + priv->status |= SNULL_TX_INTR; + if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) { + /* Simulate a dropped transmit interrupt */ + netif_stop_queue(dev); + PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies, + (unsigned long) priv->stats.tx_packets); + } + else + snull_interrupt(0, dev, NULL); +} + +/* + * Transmit a packet (called by the kernel) + */ +int snull_tx(struct sk_buff *skb, struct net_device *dev) +{ + int len; + char *data, shortpkt[ETH_ZLEN]; + struct snull_priv *priv = netdev_priv(dev); + + data = skb->data; + len = skb->len; + if (len < ETH_ZLEN) { + memset(shortpkt, 0, ETH_ZLEN); + memcpy(shortpkt, skb->data, skb->len); + len = ETH_ZLEN; + data = shortpkt; + } + dev->trans_start = jiffies; /* save the timestamp */ + + /* Remember the skb, so we can free it at interrupt time */ + priv->skb = skb; + + /* actual deliver of data is device-specific, and not shown here */ + snull_hw_tx(data, len, dev); + + return 0; /* Our simple device can not fail */ +} + +/* + * Deal with a transmit timeout. + */ +void snull_tx_timeout (struct net_device *dev) +{ + struct snull_priv *priv = netdev_priv(dev); + + PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies, + jiffies - dev->trans_start); + /* Simulate a transmission interrupt to get things moving */ + priv->status = SNULL_TX_INTR; + snull_interrupt(0, dev, NULL); + priv->stats.tx_errors++; + netif_wake_queue(dev); + return; +} + + + +/* + * Ioctl commands + */ +int snull_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + PDEBUG("ioctl\n"); + return 0; +} + +/* + * Return statistics to the caller + */ +struct net_device_stats *snull_stats(struct net_device *dev) +{ + struct snull_priv *priv = netdev_priv(dev); + return &priv->stats; +} + +/* + * This function is called to fill up an eth header, since arp is not + * available on the interface + */ +int snull_rebuild_header(struct sk_buff *skb) +{ + struct ethhdr *eth = (struct ethhdr *) skb->data; + struct net_device *dev = skb->dev; + + memcpy(eth->h_source, dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, dev->dev_addr, dev->addr_len); + eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */ + return 0; +} + + +int snull_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned int len) +{ + struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); + + eth->h_proto = htons(type); + memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len); + memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len); + eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */ + return (dev->hard_header_len); +} + + + + + +/* + * The "change_mtu" method is usually not needed. + * If you need it, it must be like this. + */ +int snull_change_mtu(struct net_device *dev, int new_mtu) +{ + unsigned long flags; + struct snull_priv *priv = netdev_priv(dev); + spinlock_t *lock = &priv->lock; + + /* check ranges */ + if ((new_mtu < 68) || (new_mtu > 1500)) + return -EINVAL; + /* + * Do anything you need, and the accept the value + */ + spin_lock_irqsave(lock, flags); + dev->mtu = new_mtu; + spin_unlock_irqrestore(lock, flags); + return 0; /* success */ +} + +/* + * The init function (sometimes called probe). + * It is invoked by register_netdev() + */ +void snull_init(struct net_device *dev) +{ + struct snull_priv *priv; +#if 0 + /* + * Make the usual checks: check_region(), probe irq, ... -ENODEV + * should be returned if no device found. No resource should be + * grabbed: this is done on open(). + */ +#endif + + /* + * Then, assign other fields in dev, using ether_setup() and some + * hand assignments + */ + ether_setup(dev); /* assign some of the fields */ + + dev->open = snull_open; + dev->stop = snull_release; + dev->set_config = snull_config; + dev->hard_start_xmit = snull_tx; + dev->do_ioctl = snull_ioctl; + dev->get_stats = snull_stats; + dev->change_mtu = snull_change_mtu; + dev->rebuild_header = snull_rebuild_header; + dev->hard_header = snull_header; + dev->tx_timeout = snull_tx_timeout; + dev->watchdog_timeo = timeout; + if (use_napi) { + dev->poll = snull_poll; + dev->weight = 2; + } + /* keep the default flags, just add NOARP */ + dev->flags |= IFF_NOARP; + dev->features |= NETIF_F_NO_CSUM; + dev->hard_header_cache = NULL; /* Disable caching */ + + /* + * Then, initialize the priv field. This encloses the statistics + * and a few private fields. + */ + priv = netdev_priv(dev); + memset(priv, 0, sizeof(struct snull_priv)); + spin_lock_init(&priv->lock); + snull_rx_ints(dev, 1); /* enable receive interrupts */ + snull_setup_pool(dev); +} + +/* + * The devices + */ + +struct net_device *snull_devs[2]; + + + +/* + * Finally, the module stuff + */ + +void snull_cleanup(void) +{ + int i; + + for (i = 0; i < 2; i++) { + if (snull_devs[i]) { + unregister_netdev(snull_devs[i]); + snull_teardown_pool(snull_devs[i]); + free_netdev(snull_devs[i]); + } + } + return; +} + + + + +int snull_init_module(void) +{ + int result, i, ret = -ENOMEM; + + snull_interrupt = use_napi ? snull_napi_interrupt : snull_regular_interrupt; + + /* Allocate the devices */ + snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d", + snull_init); + snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d", + snull_init); + if (snull_devs[0] == NULL || snull_devs[1] == NULL) + goto out; + + ret = -ENODEV; + for (i = 0; i < 2; i++) + if ((result = register_netdev(snull_devs[i]))) + printk("snull: error %i registering device \"%s\"\n", + result, snull_devs[i]->name); + else + ret = 0; + out: + if (ret) + snull_cleanup(); + return ret; +} + + +module_init(snull_init_module); +module_exit(snull_cleanup); diff --git a/examples/snull/snull.h b/examples/snull/snull.h new file mode 100644 index 0000000..69a49a0 --- /dev/null +++ b/examples/snull/snull.h @@ -0,0 +1,49 @@ + +/* + * snull.h -- definitions for the network module + * + * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet + * Copyright (C) 2001 O'Reilly & Associates + * + * The source code in this file can be freely used, adapted, + * and redistributed in source or binary form, so long as an + * acknowledgment appears in derived source files. The citation + * should list that the code comes from the book "Linux Device + * Drivers" by Alessandro Rubini and Jonathan Corbet, published + * by O'Reilly & Associates. No warranty is attached; + * we cannot take responsibility for errors or fitness for use. + */ + +/* + * Macros to help debugging + */ + +#undef PDEBUG /* undef it, just in case */ +#ifdef SNULL_DEBUG +# ifdef __KERNEL__ + /* This one if debugging is on, and kernel space */ +# define PDEBUG(fmt, args...) printk( KERN_DEBUG "snull: " fmt, ## args) +# else + /* This one for user space */ +# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) +# endif +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +#undef PDEBUGG +#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ + + +/* These are the flags in the statusword */ +#define SNULL_RX_INTR 0x0001 +#define SNULL_TX_INTR 0x0002 + +/* Default timeout period */ +#define SNULL_TIMEOUT 5 /* In jiffies */ + +extern struct net_device *snull_devs[]; + + + + diff --git a/examples/snull/snull_load b/examples/snull/snull_load new file mode 100644 index 0000000..47de578 --- /dev/null +++ b/examples/snull/snull_load @@ -0,0 +1,8 @@ +#!/bin/sh + +export PATH=/sbin:/bin + +# Use a pathname, as new modutils don't look in the current dir by default +insmod ./snull.ko $* +ifconfig sn0 local0 +ifconfig sn1 local1 diff --git a/examples/snull/snull_unload b/examples/snull/snull_unload new file mode 100644 index 0000000..a857ac9 --- /dev/null +++ b/examples/snull/snull_unload @@ -0,0 +1,5 @@ +#!/bin/sh + +/sbin/ifconfig sn0 down +/sbin/ifconfig sn1 down +/sbin/rmmod snull diff --git a/examples/tty/Makefile b/examples/tty/Makefile new file mode 100644 index 0000000..f5b8592 --- /dev/null +++ b/examples/tty/Makefile @@ -0,0 +1,41 @@ +# Comment/uncomment the following line to disable/enable debugging +#DEBUG = y + + +# Add your debugging flag (or not) to CFLAGS +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines +else + DEBFLAGS = -O2 +endif + +CFLAGS += $(DEBFLAGS) +CFLAGS += -I.. + +ifneq ($(KERNELRELEASE),) +# call from kernel build system + +obj-m := tiny_tty.o tiny_serial.o + +else + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + + + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + +depend .depend dep: + $(CC) $(CFLAGS) -M *.c > .depend + + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/examples/tty/tiny_serial.c b/examples/tty/tiny_serial.c new file mode 100644 index 0000000..1ae7a43 --- /dev/null +++ b/examples/tty/tiny_serial.c @@ -0,0 +1,291 @@ +/* + * Tiny Serial driver + * + * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This driver shows how to create a minimal serial driver. It does not rely on + * any backing hardware, but creates a timer that emulates data being received + * from some kind of hardware. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_AUTHOR "Greg Kroah-Hartman " +#define DRIVER_DESC "Tiny serial driver" + +/* Module information */ +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +#define DELAY_TIME HZ * 2 /* 2 seconds per character */ +#define TINY_DATA_CHARACTER 't' + +#define TINY_SERIAL_MAJOR 240 /* experimental range */ +#define TINY_SERIAL_MINORS 1 /* only have one minor */ +#define UART_NR 1 /* only use one port */ + +#define TINY_SERIAL_NAME "ttytiny" + +#define MY_NAME TINY_SERIAL_NAME + +static struct timer_list *timer; + +static void tiny_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ +} + +static void tiny_stop_rx(struct uart_port *port) +{ +} + +static void tiny_enable_ms(struct uart_port *port) +{ +} + +static void tiny_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + int count; + + if (port->x_char) { + pr_debug("wrote %2x", port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + tiny_stop_tx(port, 0); + return; + } + + count = port->fifosize >> 1; + do { + pr_debug("wrote %2x", xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + tiny_stop_tx(port, 0); +} + +static void tiny_start_tx(struct uart_port *port, unsigned int tty_start) +{ +} + +static void tiny_timer(unsigned long data) +{ + struct uart_port *port; + struct tty_struct *tty; + + + port = (struct uart_port *)data; + if (!port) + return; + if (!port->info) + return; + tty = port->info->tty; + if (!tty) + return; + + /* add one character to the tty port */ + /* this doesn't actually push the data through unless tty->low_latency is set */ + tty_insert_flip_char(tty, TINY_DATA_CHARACTER, 0); + + tty_flip_buffer_push(tty); + + /* resubmit the timer again */ + timer->expires = jiffies + DELAY_TIME; + add_timer(timer); + + /* see if we have any data to transmit */ + tiny_tx_chars(port); +} + +static unsigned int tiny_tx_empty(struct uart_port *port) +{ + return 0; +} + +static unsigned int tiny_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void tiny_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void tiny_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void tiny_set_termios(struct uart_port *port, + struct termios *new, struct termios *old) +{ + int baud, quot, cflag = new->c_cflag; + /* get the byte size */ + switch (cflag & CSIZE) { + case CS5: + printk(KERN_DEBUG " - data bits = 5\n"); + break; + case CS6: + printk(KERN_DEBUG " - data bits = 6\n"); + break; + case CS7: + printk(KERN_DEBUG " - data bits = 7\n"); + break; + default: // CS8 + printk(KERN_DEBUG " - data bits = 8\n"); + break; + } + + /* determine the parity */ + if (cflag & PARENB) + if (cflag & PARODD) + pr_debug(" - parity = odd\n"); + else + pr_debug(" - parity = even\n"); + else + pr_debug(" - parity = none\n"); + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + pr_debug(" - stop bits = 2\n"); + else + pr_debug(" - stop bits = 1\n"); + + /* figure out the flow control settings */ + if (cflag & CRTSCTS) + pr_debug(" - RTS/CTS is enabled\n"); + else + pr_debug(" - RTS/CTS is disabled\n"); + + /* Set baud rate */ + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + //UART_PUT_DIV_LO(port, (quot & 0xff)); + //UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); +} + +static int tiny_startup(struct uart_port *port) +{ + /* this is the first time this port is opened */ + /* do any hardware initialization needed here */ + + /* create our timer and submit it */ + if (!timer) { + timer = kmalloc(sizeof(*timer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + } + timer->data = (unsigned long)port; + timer->expires = jiffies + DELAY_TIME; + timer->function = tiny_timer; + add_timer(timer); + return 0; +} + +static void tiny_shutdown(struct uart_port *port) +{ + /* The port is being closed by the last user. */ + /* Do any hardware specific stuff here */ + + /* shut down our timer */ + del_timer(timer); +} + +static const char *tiny_type(struct uart_port *port) +{ + return "tinytty"; +} + +static void tiny_release_port(struct uart_port *port) +{ + +} + +static int tiny_request_port(struct uart_port *port) +{ + return 0; +} + +static void tiny_config_port(struct uart_port *port, int flags) +{ +} + +static int tiny_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return 0; +} + +static struct uart_ops tiny_ops = { + .tx_empty = tiny_tx_empty, + .set_mctrl = tiny_set_mctrl, + .get_mctrl = tiny_get_mctrl, + .stop_tx = tiny_stop_tx, + .start_tx = tiny_start_tx, + .stop_rx = tiny_stop_rx, + .enable_ms = tiny_enable_ms, + .break_ctl = tiny_break_ctl, + .startup = tiny_startup, + .shutdown = tiny_shutdown, + .set_termios = tiny_set_termios, + .type = tiny_type, + .release_port = tiny_release_port, + .request_port = tiny_request_port, + .config_port = tiny_config_port, + .verify_port = tiny_verify_port, +}; + +static struct uart_port tiny_port = { + .ops = &tiny_ops, +}; + +static struct uart_driver tiny_reg = { + .owner = THIS_MODULE, + .driver_name = TINY_SERIAL_NAME, + .dev_name = TINY_SERIAL_NAME, + .major = TINY_SERIAL_MAJOR, + .minor = TINY_SERIAL_MINORS, + .nr = UART_NR, +}; + +static int __init tiny_init(void) +{ + int result; + + printk(KERN_INFO "Tiny serial driver loaded\n"); + + result = uart_register_driver(&tiny_reg); + if (result) + return result; + + result = uart_add_one_port(&tiny_reg, &tiny_port); + if (result) + uart_unregister_driver(&tiny_reg); + + return result; +} + +module_init(tiny_init); diff --git a/examples/tty/tiny_tty.c b/examples/tty/tiny_tty.c new file mode 100644 index 0000000..778b207 --- /dev/null +++ b/examples/tty/tiny_tty.c @@ -0,0 +1,591 @@ +/* + * Tiny TTY driver + * + * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This driver shows how to create a minimal tty driver. It does not rely on + * any backing hardware, but creates a timer that emulates data being received + * from some kind of hardware. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_VERSION "v2.0" +#define DRIVER_AUTHOR "Greg Kroah-Hartman " +#define DRIVER_DESC "Tiny TTY driver" + +/* Module information */ +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +#define DELAY_TIME HZ * 2 /* 2 seconds per character */ +#define TINY_DATA_CHARACTER 't' + +#define TINY_TTY_MAJOR 240 /* experimental range */ +#define TINY_TTY_MINORS 4 /* only have 4 devices */ + +struct tiny_serial { + struct tty_struct *tty; /* pointer to the tty for this device */ + int open_count; /* number of times this port has been opened */ + struct semaphore sem; /* locks this structure */ + struct timer_list *timer; + + /* for tiocmget and tiocmset functions */ + int msr; /* MSR shadow */ + int mcr; /* MCR shadow */ + + /* for ioctl fun */ + struct serial_struct serial; + wait_queue_head_t wait; + struct async_icount icount; +}; + +static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */ + + +static void tiny_timer(unsigned long timer_data) +{ + struct tiny_serial *tiny = (struct tiny_serial *)timer_data; + struct tty_struct *tty; + int i; + char data[1] = {TINY_DATA_CHARACTER}; + int data_size = 1; + + if (!tiny) + return; + + tty = tiny->tty; + + /* send the data to the tty layer for users to read. This doesn't + * actually push the data through unless tty->low_latency is set */ + for (i = 0; i < data_size; ++i) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + tty_flip_buffer_push(tty); + tty_insert_flip_char(tty, data[i], TTY_NORMAL); + } + tty_flip_buffer_push(tty); + + /* resubmit the timer again */ + tiny->timer->expires = jiffies + DELAY_TIME; + add_timer(tiny->timer); +} + +static int tiny_open(struct tty_struct *tty, struct file *file) +{ + struct tiny_serial *tiny; + struct timer_list *timer; + int index; + + /* initialize the pointer in case something fails */ + tty->driver_data = NULL; + + /* get the serial object associated with this tty pointer */ + index = tty->index; + tiny = tiny_table[index]; + if (tiny == NULL) { + /* first time accessing this device, let's create it */ + tiny = kmalloc(sizeof(*tiny), GFP_KERNEL); + if (!tiny) + return -ENOMEM; + + init_MUTEX(&tiny->sem); + tiny->open_count = 0; + tiny->timer = NULL; + + tiny_table[index] = tiny; + } + + down(&tiny->sem); + + /* save our structure within the tty structure */ + tty->driver_data = tiny; + tiny->tty = tty; + + ++tiny->open_count; + if (tiny->open_count == 1) { + /* this is the first time this port is opened */ + /* do any hardware initialization needed here */ + + /* create our timer and submit it */ + if (!tiny->timer) { + timer = kmalloc(sizeof(*timer), GFP_KERNEL); + if (!timer) { + up(&tiny->sem); + return -ENOMEM; + } + tiny->timer = timer; + } + tiny->timer->data = (unsigned long )tiny; + tiny->timer->expires = jiffies + DELAY_TIME; + tiny->timer->function = tiny_timer; + add_timer(tiny->timer); + } + + up(&tiny->sem); + return 0; +} + +static void do_close(struct tiny_serial *tiny) +{ + down(&tiny->sem); + + if (!tiny->open_count) { + /* port was never opened */ + goto exit; + } + + --tiny->open_count; + if (tiny->open_count <= 0) { + /* The port is being closed by the last user. */ + /* Do any hardware specific stuff here */ + + /* shut down our timer */ + del_timer(tiny->timer); + } +exit: + up(&tiny->sem); +} + +static void tiny_close(struct tty_struct *tty, struct file *file) +{ + struct tiny_serial *tiny = tty->driver_data; + + if (tiny) + do_close(tiny); +} + +static int tiny_write(struct tty_struct *tty, + const unsigned char *buffer, int count) +{ + struct tiny_serial *tiny = tty->driver_data; + int i; + int retval = -EINVAL; + + if (!tiny) + return -ENODEV; + + down(&tiny->sem); + + if (!tiny->open_count) + /* port was not opened */ + goto exit; + + /* fake sending the data out a hardware port by + * writing it to the kernel debug log. + */ + printk(KERN_DEBUG "%s - ", __FUNCTION__); + for (i = 0; i < count; ++i) + printk("%02x ", buffer[i]); + printk("\n"); + +exit: + up(&tiny->sem); + return retval; +} + +static int tiny_write_room(struct tty_struct *tty) +{ + struct tiny_serial *tiny = tty->driver_data; + int room = -EINVAL; + + if (!tiny) + return -ENODEV; + + down(&tiny->sem); + + if (!tiny->open_count) { + /* port was not opened */ + goto exit; + } + + /* calculate how much room is left in the device */ + room = 255; + +exit: + up(&tiny->sem); + return room; +} + +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +static void tiny_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + unsigned int cflag; + + cflag = tty->termios->c_cflag; + + /* check that they really want us to change something */ + if (old_termios) { + if ((cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(tty->termios->c_iflag) == + RELEVANT_IFLAG(old_termios->c_iflag))) { + printk(KERN_DEBUG " - nothing to change...\n"); + return; + } + } + + /* get the byte size */ + switch (cflag & CSIZE) { + case CS5: + printk(KERN_DEBUG " - data bits = 5\n"); + break; + case CS6: + printk(KERN_DEBUG " - data bits = 6\n"); + break; + case CS7: + printk(KERN_DEBUG " - data bits = 7\n"); + break; + default: + case CS8: + printk(KERN_DEBUG " - data bits = 8\n"); + break; + } + + /* determine the parity */ + if (cflag & PARENB) + if (cflag & PARODD) + printk(KERN_DEBUG " - parity = odd\n"); + else + printk(KERN_DEBUG " - parity = even\n"); + else + printk(KERN_DEBUG " - parity = none\n"); + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + printk(KERN_DEBUG " - stop bits = 2\n"); + else + printk(KERN_DEBUG " - stop bits = 1\n"); + + /* figure out the hardware flow control settings */ + if (cflag & CRTSCTS) + printk(KERN_DEBUG " - RTS/CTS is enabled\n"); + else + printk(KERN_DEBUG " - RTS/CTS is disabled\n"); + + /* determine software flow control */ + /* if we are implementing XON/XOFF, set the start and + * stop character in the device */ + if (I_IXOFF(tty) || I_IXON(tty)) { + unsigned char stop_char = STOP_CHAR(tty); + unsigned char start_char = START_CHAR(tty); + + /* if we are implementing INBOUND XON/XOFF */ + if (I_IXOFF(tty)) + printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, " + "XON = %2x, XOFF = %2x", start_char, stop_char); + else + printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled"); + + /* if we are implementing OUTBOUND XON/XOFF */ + if (I_IXON(tty)) + printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, " + "XON = %2x, XOFF = %2x", start_char, stop_char); + else + printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled"); + } + + /* get the baud rate wanted */ + printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty)); +} + +/* Our fake UART values */ +#define MCR_DTR 0x01 +#define MCR_RTS 0x02 +#define MCR_LOOP 0x04 +#define MSR_CTS 0x08 +#define MSR_CD 0x10 +#define MSR_RI 0x20 +#define MSR_DSR 0x40 + +static int tiny_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct tiny_serial *tiny = tty->driver_data; + + unsigned int result = 0; + unsigned int msr = tiny->msr; + unsigned int mcr = tiny->mcr; + + result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */ + ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */ + ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */ + ((msr & MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */ + ((msr & MSR_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/ + ((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */ + ((msr & MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */ + + return result; +} + +static int tiny_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct tiny_serial *tiny = tty->driver_data; + unsigned int mcr = tiny->mcr; + + if (set & TIOCM_RTS) + mcr |= MCR_RTS; + if (set & TIOCM_DTR) + mcr |= MCR_RTS; + + if (clear & TIOCM_RTS) + mcr &= ~MCR_RTS; + if (clear & TIOCM_DTR) + mcr &= ~MCR_RTS; + + /* set the new MCR value in the device */ + tiny->mcr = mcr; + return 0; +} + +static int tiny_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct tiny_serial *tiny; + off_t begin = 0; + int length = 0; + int i; + + length += sprintf(page, "tinyserinfo:1.0 driver:%s\n", DRIVER_VERSION); + for (i = 0; i < TINY_TTY_MINORS && length < PAGE_SIZE; ++i) { + tiny = tiny_table[i]; + if (tiny == NULL) + continue; + + length += sprintf(page+length, "%d\n", i); + if ((length + begin) > (off + count)) + goto done; + if ((length + begin) < off) { + begin += length; + length = 0; + } + } + *eof = 1; +done: + if (off >= (length + begin)) + return 0; + *start = page + (off-begin); + return (count < begin+length-off) ? count : begin + length-off; +} + +#define tiny_ioctl tiny_ioctl_tiocgserial +static int tiny_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tiny_serial *tiny = tty->driver_data; + + if (cmd == TIOCGSERIAL) { + struct serial_struct tmp; + + if (!arg) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.type = tiny->serial.type; + tmp.line = tiny->serial.line; + tmp.port = tiny->serial.port; + tmp.irq = tiny->serial.irq; + tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; + tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size; + tmp.baud_base = tiny->serial.baud_base; + tmp.close_delay = 5*HZ; + tmp.closing_wait = 30*HZ; + tmp.custom_divisor = tiny->serial.custom_divisor; + tmp.hub6 = tiny->serial.hub6; + tmp.io_type = tiny->serial.io_type; + + if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct))) + return -EFAULT; + return 0; + } + return -ENOIOCTLCMD; +} +#undef tiny_ioctl + +#define tiny_ioctl tiny_ioctl_tiocmiwait +static int tiny_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tiny_serial *tiny = tty->driver_data; + + if (cmd == TIOCMIWAIT) { + DECLARE_WAITQUEUE(wait, current); + struct async_icount cnow; + struct async_icount cprev; + + cprev = tiny->icount; + while (1) { + add_wait_queue(&tiny->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + remove_wait_queue(&tiny->wait, &wait); + + /* see if a signal woke us up */ + if (signal_pending(current)) + return -ERESTARTSYS; + + cnow = tiny->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + + } + return -ENOIOCTLCMD; +} +#undef tiny_ioctl + +#define tiny_ioctl tiny_ioctl_tiocgicount +static int tiny_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tiny_serial *tiny = tty->driver_data; + + if (cmd == TIOCGICOUNT) { + struct async_icount cnow = tiny->icount; + struct serial_icounter_struct icount; + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void __user *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + } + return -ENOIOCTLCMD; +} +#undef tiny_ioctl + +/* the real tiny_ioctl function. The above is done to get the small functions in the book */ +static int tiny_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case TIOCGSERIAL: + return tiny_ioctl_tiocgserial(tty, file, cmd, arg); + case TIOCMIWAIT: + return tiny_ioctl_tiocmiwait(tty, file, cmd, arg); + case TIOCGICOUNT: + return tiny_ioctl_tiocgicount(tty, file, cmd, arg); + } + + return -ENOIOCTLCMD; +} + +static struct tty_operations serial_ops = { + .open = tiny_open, + .close = tiny_close, + .write = tiny_write, + .write_room = tiny_write_room, + .set_termios = tiny_set_termios, +}; + +static struct tty_driver *tiny_tty_driver; + +static int __init tiny_init(void) +{ + int retval; + int i; + + /* allocate the tty driver */ + tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS); + if (!tiny_tty_driver) + return -ENOMEM; + + /* initialize the tty driver */ + tiny_tty_driver->owner = THIS_MODULE; + tiny_tty_driver->driver_name = "tiny_tty"; + tiny_tty_driver->name = "ttty"; + tiny_tty_driver->devfs_name = "tts/ttty%d"; + tiny_tty_driver->major = TINY_TTY_MAJOR, + tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, + tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL, + tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + tiny_tty_driver->init_termios = tty_std_termios; + tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + tty_set_operations(tiny_tty_driver, &serial_ops); + + /* hack to make the book purty, yet still use these functions in the + * real driver. They really should be set up in the serial_ops + * structure above... */ + tiny_tty_driver->read_proc = tiny_read_proc; + tiny_tty_driver->tiocmget = tiny_tiocmget; + tiny_tty_driver->tiocmset = tiny_tiocmset; + tiny_tty_driver->ioctl = tiny_ioctl; + + /* register the tty driver */ + retval = tty_register_driver(tiny_tty_driver); + if (retval) { + printk(KERN_ERR "failed to register tiny tty driver"); + put_tty_driver(tiny_tty_driver); + return retval; + } + + for (i = 0; i < TINY_TTY_MINORS; ++i) + tty_register_device(tiny_tty_driver, i, NULL); + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION); + return retval; +} + +static void __exit tiny_exit(void) +{ + struct tiny_serial *tiny; + int i; + + for (i = 0; i < TINY_TTY_MINORS; ++i) + tty_unregister_device(tiny_tty_driver, i); + tty_unregister_driver(tiny_tty_driver); + + /* shut down all of the timers and free the memory */ + for (i = 0; i < TINY_TTY_MINORS; ++i) { + tiny = tiny_table[i]; + if (tiny) { + /* close the port */ + while (tiny->open_count) + do_close(tiny); + + /* shut down our timer and free the memory */ + del_timer(tiny->timer); + kfree(tiny->timer); + kfree(tiny); + tiny_table[i] = NULL; + } + } +} + +module_init(tiny_init); +module_exit(tiny_exit); diff --git a/examples/usb/Makefile b/examples/usb/Makefile new file mode 100644 index 0000000..028111f --- /dev/null +++ b/examples/usb/Makefile @@ -0,0 +1,11 @@ +obj-m := usb-skeleton.o + +KERNELDIR ?= /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KERNELDIR) M=$(PWD) + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + diff --git a/examples/usb/usb-skeleton.c b/examples/usb/usb-skeleton.c new file mode 100644 index 0000000..625bc0b --- /dev/null +++ b/examples/usb/usb-skeleton.c @@ -0,0 +1,356 @@ +/* + * USB Skeleton driver - 2.0 + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c + * but has been rewritten to be easy to read and use, as no locks are now + * needed anymore. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Define these values to match your devices */ +#define USB_SKEL_VENDOR_ID 0xfff0 +#define USB_SKEL_PRODUCT_ID 0xfff0 + +/* table of devices that work with this driver */ +static struct usb_device_id skel_table [] = { + { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, skel_table); + + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_SKEL_MINOR_BASE 192 + +/* Structure to hold all of our device specific stuff */ +struct usb_skel { + struct usb_device * udev; /* the usb device for this device */ + struct usb_interface * interface; /* the interface for this device */ + unsigned char * bulk_in_buffer; /* the buffer to receive data */ + size_t bulk_in_size; /* the size of the receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + struct kref kref; +}; +#define to_skel_dev(d) container_of(d, struct usb_skel, kref) + +static struct usb_driver skel_driver; + +static void skel_delete(struct kref *kref) +{ + struct usb_skel *dev = to_skel_dev(kref); + + usb_put_dev(dev->udev); + kfree (dev->bulk_in_buffer); + kfree (dev); +} + +static int skel_open(struct inode *inode, struct file *file) +{ + struct usb_skel *dev; + struct usb_interface *interface; + int subminor; + int retval = 0; + + subminor = iminor(inode); + + interface = usb_find_interface(&skel_driver, subminor); + if (!interface) { + err ("%s - error, can't find device for minor %d", + __FUNCTION__, subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit; + } + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + /* save our object in the file's private structure */ + file->private_data = dev; + +exit: + return retval; +} + +static int skel_release(struct inode *inode, struct file *file) +{ + struct usb_skel *dev; + + dev = (struct usb_skel *)file->private_data; + if (dev == NULL) + return -ENODEV; + + /* decrement the count on our device */ + kref_put(&dev->kref, skel_delete); + return 0; +} + +static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct usb_skel *dev; + int retval = 0; + + dev = (struct usb_skel *)file->private_data; + + /* do a blocking bulk read to get data from the device */ + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + dev->bulk_in_buffer, + min(dev->bulk_in_size, count), + &count, HZ*10); + + /* if the read was successful, copy the data to userspace */ + if (!retval) { + if (copy_to_user(buffer, dev->bulk_in_buffer, count)) + retval = -EFAULT; + else + retval = count; + } + + return retval; +} + +static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + /* sync/async unlink faults aren't errors */ + if (urb->status && + !(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + } + + /* free up our allocated buffer */ + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); +} + +static ssize_t skel_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) +{ + struct usb_skel *dev; + int retval = 0; + struct urb *urb = NULL; + char *buf = NULL; + + dev = (struct usb_skel *)file->private_data; + + /* verify that we actually have some data to write */ + if (count == 0) + goto exit; + + /* create a urb, and a buffer for it, and copy the data to the urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } + + buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + goto error; + } + if (copy_from_user(buf, user_buffer, count)) { + retval = -EFAULT; + goto error; + } + + /* initialize the urb properly */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + buf, count, skel_write_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* send the data out the bulk port */ + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); + goto error; + } + + /* release our reference to this urb, the USB core will eventually free it entirely */ + usb_free_urb(urb); + +exit: + return count; + +error: + usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); + usb_free_urb(urb); + kfree(buf); + return retval; +} + +static struct file_operations skel_fops = { + .owner = THIS_MODULE, + .read = skel_read, + .write = skel_write, + .open = skel_open, + .release = skel_release, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver skel_class = { + .name = "usb/skel%d", + .fops = &skel_fops, + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, + .minor_base = USB_SKEL_MINOR_BASE, +}; + +static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_skel *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kmalloc(sizeof(struct usb_skel), GFP_KERNEL); + if (dev == NULL) { + err("Out of memory"); + goto error; + } + memset(dev, 0x00, sizeof (*dev)); + kref_init(&dev->kref); + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + (endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + buffer_size = endpoint->wMaxPacketSize; + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!dev->bulk_in_buffer) { + err("Could not allocate bulk_in_buffer"); + goto error; + } + } + + if (!dev->bulk_out_endpointAddr && + !(endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk out endpoint */ + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; + } + } + if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { + err("Could not find both bulk-in and bulk-out endpoints"); + goto error; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* we can register the device now, as it is ready */ + retval = usb_register_dev(interface, &skel_class); + if (retval) { + /* something prevented us from registering this driver */ + err("Not able to get a minor for this device."); + usb_set_intfdata(interface, NULL); + goto error; + } + + /* let the user know what node this device is now attached to */ + info("USB Skeleton device now attached to USBSkel-%d", interface->minor); + return 0; + +error: + if (dev) + kref_put(&dev->kref, skel_delete); + return retval; +} + +static void skel_disconnect(struct usb_interface *interface) +{ + struct usb_skel *dev; + int minor = interface->minor; + + /* prevent skel_open() from racing skel_disconnect() */ + lock_kernel(); + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* give back our minor */ + usb_deregister_dev(interface, &skel_class); + + unlock_kernel(); + + /* decrement our usage count */ + kref_put(&dev->kref, skel_delete); + + info("USB Skeleton #%d now disconnected", minor); +} + +static struct usb_driver skel_driver = { + .owner = THIS_MODULE, + .name = "skeleton", + .id_table = skel_table, + .probe = skel_probe, + .disconnect = skel_disconnect, +}; + +static int __init usb_skel_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&skel_driver); + if (result) + err("usb_register failed. Error number %d", result); + + return result; +} + +static void __exit usb_skel_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&skel_driver); +} + +module_init (usb_skel_init); +module_exit (usb_skel_exit); + +MODULE_LICENSE("GPL"); diff --git a/hellomodule/Makefile b/hellomodule/Makefile new file mode 100644 index 0000000..3827f91 --- /dev/null +++ b/hellomodule/Makefile @@ -0,0 +1,17 @@ +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +obj-m := hellomodule.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean copy + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + +copy: + sudo cp *.ko /home/embedfire/workdir \ No newline at end of file diff --git a/hellomodule/hellomodule.c b/hellomodule/hellomodule.c new file mode 100644 index 0000000..ff4a00c --- /dev/null +++ b/hellomodule/hellomodule.c @@ -0,0 +1,23 @@ +#include +#include +#include + +static int __init hello_init(void) + { + printk(KERN_EMERG "[ KERN_EMERG ] Hello Module Init\n"); + printk( "[ default ] Hello Module Init\n"); + return 0; +} + +static void __exit hello_exit(void) +{ + printk("[ default ] Hello Module Exit\n"); +} + +module_init(hello_init); +module_exit(hello_exit); + +//MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("embedfire "); +MODULE_DESCRIPTION("hello world module"); +MODULE_ALIAS("test_module"); \ No newline at end of file diff --git a/hellow/.Module.symvers.cmd b/hellow/.Module.symvers.cmd new file mode 100644 index 0000000..ffc1d96 --- /dev/null +++ b/hellow/.Module.symvers.cmd @@ -0,0 +1 @@ +cmd_/home/chao/code/module/hellow/Module.symvers := sed 's/ko$$/o/' /home/chao/code/module/hellow/modules.order | scripts/mod/modpost -m -a -o /home/chao/code/module/hellow/Module.symvers -e -i Module.symvers -T - diff --git a/hellow/.hello.ko.cmd b/hellow/.hello.ko.cmd new file mode 100644 index 0000000..0f81c32 --- /dev/null +++ b/hellow/.hello.ko.cmd @@ -0,0 +1 @@ +cmd_/home/chao/code/module/hellow/hello.ko := ld -r -m elf_x86_64 -z max-page-size=0x200000 --build-id=sha1 -T ./scripts/module-common.lds -o /home/chao/code/module/hellow/hello.ko /home/chao/code/module/hellow/hello.o /home/chao/code/module/hellow/hello.mod.o; true diff --git a/hellow/.hello.mod.cmd b/hellow/.hello.mod.cmd new file mode 100644 index 0000000..3beb63c --- /dev/null +++ b/hellow/.hello.mod.cmd @@ -0,0 +1 @@ +cmd_/home/chao/code/module/hellow/hello.mod := { echo /home/chao/code/module/hellow/hello.o; echo; } > /home/chao/code/module/hellow/hello.mod diff --git a/hellow/.hello.mod.o.cmd b/hellow/.hello.mod.o.cmd new file mode 100644 index 0000000..4753468 --- /dev/null +++ b/hellow/.hello.mod.o.cmd @@ -0,0 +1,694 @@ +cmd_/home/chao/code/module/hellow/hello.mod.o := gcc -Wp,-MMD,/home/chao/code/module/hellow/.hello.mod.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/9/include -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -Iubuntu/include -include ./include/linux/compiler_types.h -D__KERNEL__ -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -DCONFIG_X86_X32_ABI -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fstack-protector-strong -Wno-unused-but-set-variable -Wimplicit-fallthrough -Wno-unused-const-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-var-tracking-assignments -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-stringop-truncation -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -fmacro-prefix-map=./= -Wno-packed-not-aligned -DMODULE -DKBUILD_BASENAME='"hello.mod"' -DKBUILD_MODNAME='"hello"' -c -o /home/chao/code/module/hellow/hello.mod.o /home/chao/code/module/hellow/hello.mod.c + +source_/home/chao/code/module/hellow/hello.mod.o := /home/chao/code/module/hellow/hello.mod.c + +deps_/home/chao/code/module/hellow/hello.mod.o := \ + $(wildcard include/config/module/unload.h) \ + $(wildcard include/config/retpoline.h) \ + include/linux/kconfig.h \ + $(wildcard include/config/cc/version/text.h) \ + $(wildcard include/config/cpu/big/endian.h) \ + $(wildcard include/config/booger.h) \ + $(wildcard include/config/foo.h) \ + include/linux/compiler_types.h \ + $(wildcard include/config/have/arch/compiler/h.h) \ + $(wildcard include/config/enable/must/check.h) \ + $(wildcard include/config/cc/has/asm/inline.h) \ + include/linux/compiler_attributes.h \ + include/linux/compiler-gcc.h \ + $(wildcard include/config/arm64.h) \ + $(wildcard include/config/arch/use/builtin/bswap.h) \ + include/linux/module.h \ + $(wildcard include/config/modules.h) \ + $(wildcard include/config/sysfs.h) \ + $(wildcard include/config/modules/tree/lookup.h) \ + $(wildcard include/config/livepatch.h) \ + $(wildcard include/config/unused/symbols.h) \ + $(wildcard include/config/module/sig.h) \ + $(wildcard include/config/generic/bug.h) \ + $(wildcard include/config/kallsyms.h) \ + $(wildcard include/config/smp.h) \ + $(wildcard include/config/tracepoints.h) \ + $(wildcard include/config/tree/srcu.h) \ + $(wildcard include/config/bpf/events.h) \ + $(wildcard include/config/jump/label.h) \ + $(wildcard include/config/tracing.h) \ + $(wildcard include/config/event/tracing.h) \ + $(wildcard include/config/ftrace/mcount/record.h) \ + $(wildcard include/config/kprobes.h) \ + $(wildcard include/config/constructors.h) \ + $(wildcard include/config/function/error/injection.h) \ + include/linux/list.h \ + $(wildcard include/config/debug/list.h) \ + include/linux/types.h \ + $(wildcard include/config/have/uid16.h) \ + $(wildcard include/config/uid16.h) \ + $(wildcard include/config/arch/dma/addr/t/64bit.h) \ + $(wildcard include/config/phys/addr/t/64bit.h) \ + $(wildcard include/config/64bit.h) \ + include/uapi/linux/types.h \ + arch/x86/include/generated/uapi/asm/types.h \ + include/uapi/asm-generic/types.h \ + include/asm-generic/int-ll64.h \ + include/uapi/asm-generic/int-ll64.h \ + arch/x86/include/uapi/asm/bitsperlong.h \ + include/asm-generic/bitsperlong.h \ + include/uapi/asm-generic/bitsperlong.h \ + include/uapi/linux/posix_types.h \ + include/linux/stddef.h \ + include/uapi/linux/stddef.h \ + include/linux/compiler_types.h \ + arch/x86/include/asm/posix_types.h \ + $(wildcard include/config/x86/32.h) \ + arch/x86/include/uapi/asm/posix_types_64.h \ + include/uapi/asm-generic/posix_types.h \ + include/linux/poison.h \ + $(wildcard include/config/illegal/pointer/value.h) \ + $(wildcard include/config/page/poisoning/zero.h) \ + include/linux/const.h \ + include/vdso/const.h \ + include/uapi/linux/const.h \ + include/linux/kernel.h \ + $(wildcard include/config/preempt/voluntary.h) \ + $(wildcard include/config/debug/atomic/sleep.h) \ + $(wildcard include/config/preempt/rt.h) \ + $(wildcard include/config/mmu.h) \ + $(wildcard include/config/prove/locking.h) \ + $(wildcard include/config/panic/timeout.h) \ + include/linux/limits.h \ + include/uapi/linux/limits.h \ + include/vdso/limits.h \ + include/linux/linkage.h \ + $(wildcard include/config/arch/use/sym/annotations.h) \ + include/linux/stringify.h \ + include/linux/export.h \ + $(wildcard include/config/modversions.h) \ + $(wildcard include/config/module/rel/crcs.h) \ + $(wildcard include/config/have/arch/prel32/relocations.h) \ + $(wildcard include/config/trim/unused/ksyms.h) \ + include/linux/compiler.h \ + $(wildcard include/config/trace/branch/profiling.h) \ + $(wildcard include/config/profile/all/branches.h) \ + $(wildcard include/config/stack/validation.h) \ + $(wildcard include/config/debug/entry.h) \ + arch/x86/include/asm/barrier.h \ + arch/x86/include/asm/alternative.h \ + arch/x86/include/asm/asm.h \ + arch/x86/include/asm/nops.h \ + $(wildcard include/config/mk7.h) \ + $(wildcard include/config/x86/p6/nop.h) \ + $(wildcard include/config/x86/64.h) \ + include/asm-generic/barrier.h \ + include/linux/kasan-checks.h \ + $(wildcard include/config/kasan.h) \ + include/linux/kcsan-checks.h \ + $(wildcard include/config/kcsan.h) \ + $(wildcard include/config/kcsan/ignore/atomics.h) \ + arch/x86/include/asm/linkage.h \ + $(wildcard include/config/x86/alignment/16.h) \ + include/linux/bitops.h \ + include/linux/bits.h \ + include/vdso/bits.h \ + include/linux/build_bug.h \ + arch/x86/include/asm/bitops.h \ + $(wildcard include/config/x86/cmov.h) \ + arch/x86/include/asm/rmwcc.h \ + $(wildcard include/config/cc/has/asm/goto.h) \ + include/asm-generic/bitops/find.h \ + $(wildcard include/config/generic/find/first/bit.h) \ + include/asm-generic/bitops/sched.h \ + arch/x86/include/asm/arch_hweight.h \ + arch/x86/include/asm/cpufeatures.h \ + arch/x86/include/asm/required-features.h \ + $(wildcard include/config/x86/minimum/cpu/family.h) \ + $(wildcard include/config/math/emulation.h) \ + $(wildcard include/config/x86/pae.h) \ + $(wildcard include/config/x86/cmpxchg64.h) \ + $(wildcard include/config/x86/use/3dnow.h) \ + $(wildcard include/config/matom.h) \ + $(wildcard include/config/paravirt.h) \ + arch/x86/include/asm/disabled-features.h \ + $(wildcard include/config/x86/smap.h) \ + $(wildcard include/config/x86/umip.h) \ + $(wildcard include/config/x86/intel/memory/protection/keys.h) \ + $(wildcard include/config/x86/5level.h) \ + $(wildcard include/config/page/table/isolation.h) \ + include/asm-generic/bitops/const_hweight.h \ + include/asm-generic/bitops/instrumented-atomic.h \ + include/linux/instrumented.h \ + include/asm-generic/bitops/instrumented-non-atomic.h \ + include/asm-generic/bitops/instrumented-lock.h \ + include/asm-generic/bitops/le.h \ + arch/x86/include/uapi/asm/byteorder.h \ + include/linux/byteorder/little_endian.h \ + include/uapi/linux/byteorder/little_endian.h \ + include/linux/swab.h \ + include/uapi/linux/swab.h \ + arch/x86/include/uapi/asm/swab.h \ + include/linux/byteorder/generic.h \ + include/asm-generic/bitops/ext2-atomic-setbit.h \ + include/linux/log2.h \ + $(wildcard include/config/arch/has/ilog2/u32.h) \ + $(wildcard include/config/arch/has/ilog2/u64.h) \ + include/linux/typecheck.h \ + include/linux/printk.h \ + $(wildcard include/config/message/loglevel/default.h) \ + $(wildcard include/config/console/loglevel/default.h) \ + $(wildcard include/config/console/loglevel/quiet.h) \ + $(wildcard include/config/early/printk.h) \ + $(wildcard include/config/printk/nmi.h) \ + $(wildcard include/config/printk.h) \ + $(wildcard include/config/kmsg/ids.h) \ + $(wildcard include/config/dynamic/debug.h) \ + $(wildcard include/config/dynamic/debug/core.h) \ + include/linux/init.h \ + $(wildcard include/config/strict/kernel/rwx.h) \ + $(wildcard include/config/strict/module/rwx.h) \ + include/linux/kern_levels.h \ + include/linux/cache.h \ + $(wildcard include/config/arch/has/cache/line/size.h) \ + include/uapi/linux/kernel.h \ + include/uapi/linux/sysinfo.h \ + arch/x86/include/asm/cache.h \ + $(wildcard include/config/x86/l1/cache/shift.h) \ + $(wildcard include/config/x86/internode/cache/shift.h) \ + $(wildcard include/config/x86/vsmp.h) \ + include/linux/dynamic_debug.h \ + include/linux/jump_label.h \ + $(wildcard include/config/have/arch/jump/label/relative.h) \ + arch/x86/include/asm/jump_label.h \ + arch/x86/include/asm/div64.h \ + include/asm-generic/div64.h \ + include/linux/stat.h \ + arch/x86/include/uapi/asm/stat.h \ + include/uapi/linux/stat.h \ + include/linux/time.h \ + $(wildcard include/config/arch/uses/gettimeoffset.h) \ + $(wildcard include/config/posix/timers.h) \ + include/linux/seqlock.h \ + $(wildcard include/config/debug/lock/alloc.h) \ + include/linux/spinlock.h \ + $(wildcard include/config/debug/spinlock.h) \ + $(wildcard include/config/preemption.h) \ + include/linux/preempt.h \ + $(wildcard include/config/preempt/count.h) \ + $(wildcard include/config/debug/preempt.h) \ + $(wildcard include/config/trace/preempt/toggle.h) \ + $(wildcard include/config/preempt/notifiers.h) \ + arch/x86/include/asm/preempt.h \ + arch/x86/include/asm/percpu.h \ + $(wildcard include/config/x86/64/smp.h) \ + include/asm-generic/percpu.h \ + $(wildcard include/config/have/setup/per/cpu/area.h) \ + include/linux/threads.h \ + $(wildcard include/config/nr/cpus.h) \ + $(wildcard include/config/base/small.h) \ + include/linux/percpu-defs.h \ + $(wildcard include/config/debug/force/weak/per/cpu.h) \ + $(wildcard include/config/amd/mem/encrypt.h) \ + include/linux/thread_info.h \ + $(wildcard include/config/thread/info/in/task.h) \ + $(wildcard include/config/have/arch/within/stack/frames.h) \ + $(wildcard include/config/hardened/usercopy.h) \ + include/linux/bug.h \ + $(wildcard include/config/bug/on/data/corruption.h) \ + arch/x86/include/asm/bug.h \ + $(wildcard include/config/debug/bugverbose.h) \ + include/asm-generic/bug.h \ + $(wildcard include/config/bug.h) \ + $(wildcard include/config/generic/bug/relative/pointers.h) \ + include/linux/restart_block.h \ + include/linux/time64.h \ + include/linux/math64.h \ + $(wildcard include/config/arch/supports/int128.h) \ + include/vdso/math64.h \ + include/vdso/time64.h \ + include/uapi/linux/time.h \ + include/uapi/linux/time_types.h \ + arch/x86/include/asm/current.h \ + arch/x86/include/asm/thread_info.h \ + $(wildcard include/config/vm86.h) \ + $(wildcard include/config/x86/iopl/ioperm.h) \ + $(wildcard include/config/frame/pointer.h) \ + $(wildcard include/config/compat.h) \ + $(wildcard include/config/ia32/emulation.h) \ + arch/x86/include/asm/page.h \ + arch/x86/include/asm/page_types.h \ + $(wildcard include/config/physical/start.h) \ + $(wildcard include/config/physical/align.h) \ + $(wildcard include/config/dynamic/physical/mask.h) \ + include/linux/mem_encrypt.h \ + $(wildcard include/config/arch/has/mem/encrypt.h) \ + arch/x86/include/asm/mem_encrypt.h \ + arch/x86/include/uapi/asm/bootparam.h \ + include/linux/screen_info.h \ + include/uapi/linux/screen_info.h \ + include/linux/apm_bios.h \ + include/uapi/linux/apm_bios.h \ + include/uapi/linux/ioctl.h \ + arch/x86/include/generated/uapi/asm/ioctl.h \ + include/asm-generic/ioctl.h \ + include/uapi/asm-generic/ioctl.h \ + include/linux/edd.h \ + include/uapi/linux/edd.h \ + arch/x86/include/asm/ist.h \ + arch/x86/include/uapi/asm/ist.h \ + include/video/edid.h \ + $(wildcard include/config/x86.h) \ + include/uapi/video/edid.h \ + arch/x86/include/asm/page_64_types.h \ + $(wildcard include/config/dynamic/memory/layout.h) \ + $(wildcard include/config/randomize/base.h) \ + arch/x86/include/asm/kaslr.h \ + $(wildcard include/config/randomize/memory.h) \ + arch/x86/include/asm/page_64.h \ + $(wildcard include/config/debug/virtual.h) \ + $(wildcard include/config/flatmem.h) \ + $(wildcard include/config/x86/vsyscall/emulation.h) \ + include/linux/range.h \ + include/asm-generic/memory_model.h \ + $(wildcard include/config/discontigmem.h) \ + $(wildcard include/config/sparsemem/vmemmap.h) \ + $(wildcard include/config/sparsemem.h) \ + include/linux/pfn.h \ + include/asm-generic/getorder.h \ + arch/x86/include/asm/cpufeature.h \ + $(wildcard include/config/x86/feature/names.h) \ + arch/x86/include/asm/processor.h \ + $(wildcard include/config/x86/vmx/feature/names.h) \ + $(wildcard include/config/kvm.h) \ + $(wildcard include/config/stackprotector.h) \ + $(wildcard include/config/paravirt/xxl.h) \ + $(wildcard include/config/x86/debugctlmsr.h) \ + $(wildcard include/config/cpu/sup/amd.h) \ + $(wildcard include/config/xen.h) \ + arch/x86/include/asm/processor-flags.h \ + arch/x86/include/uapi/asm/processor-flags.h \ + arch/x86/include/asm/math_emu.h \ + arch/x86/include/asm/ptrace.h \ + arch/x86/include/asm/segment.h \ + $(wildcard include/config/xen/pv.h) \ + $(wildcard include/config/x86/32/lazy/gs.h) \ + arch/x86/include/uapi/asm/ptrace.h \ + arch/x86/include/uapi/asm/ptrace-abi.h \ + arch/x86/include/asm/paravirt_types.h \ + $(wildcard include/config/pgtable/levels.h) \ + $(wildcard include/config/paravirt/debug.h) \ + arch/x86/include/asm/desc_defs.h \ + arch/x86/include/asm/kmap_types.h \ + $(wildcard include/config/debug/highmem.h) \ + include/asm-generic/kmap_types.h \ + arch/x86/include/asm/pgtable_types.h \ + $(wildcard include/config/mem/soft/dirty.h) \ + $(wildcard include/config/have/arch/userfaultfd/wp.h) \ + $(wildcard include/config/proc/fs.h) \ + arch/x86/include/asm/pgtable_64_types.h \ + arch/x86/include/asm/sparsemem.h \ + arch/x86/include/asm/nospec-branch.h \ + include/linux/static_key.h \ + include/linux/frame.h \ + arch/x86/include/asm/alternative-asm.h \ + arch/x86/include/asm/msr-index.h \ + arch/x86/include/asm/unwind_hints.h \ + arch/x86/include/asm/orc_types.h \ + arch/x86/include/asm/spinlock_types.h \ + include/asm-generic/qspinlock_types.h \ + include/asm-generic/qrwlock_types.h \ + arch/x86/include/uapi/asm/sigcontext.h \ + arch/x86/include/asm/msr.h \ + arch/x86/include/asm/msr-index.h \ + arch/x86/include/generated/uapi/asm/errno.h \ + include/uapi/asm-generic/errno.h \ + include/uapi/asm-generic/errno-base.h \ + arch/x86/include/asm/cpumask.h \ + include/linux/cpumask.h \ + $(wildcard include/config/cpumask/offstack.h) \ + $(wildcard include/config/hotplug/cpu.h) \ + $(wildcard include/config/debug/per/cpu/maps.h) \ + include/linux/bitmap.h \ + include/linux/string.h \ + $(wildcard include/config/binary/printf.h) \ + $(wildcard include/config/fortify/source.h) \ + include/uapi/linux/string.h \ + arch/x86/include/asm/string.h \ + arch/x86/include/asm/string_64.h \ + $(wildcard include/config/arch/has/uaccess/flushcache.h) \ + include/linux/atomic.h \ + arch/x86/include/asm/atomic.h \ + arch/x86/include/asm/cmpxchg.h \ + arch/x86/include/asm/cmpxchg_64.h \ + arch/x86/include/asm/atomic64_64.h \ + include/linux/atomic-arch-fallback.h \ + $(wildcard include/config/generic/atomic64.h) \ + include/asm-generic/atomic-instrumented.h \ + include/asm-generic/atomic-long.h \ + arch/x86/include/uapi/asm/msr.h \ + include/linux/tracepoint-defs.h \ + arch/x86/include/asm/paravirt.h \ + $(wildcard include/config/paravirt/spinlocks.h) \ + arch/x86/include/asm/frame.h \ + arch/x86/include/asm/special_insns.h \ + include/linux/irqflags.h \ + $(wildcard include/config/trace/irqflags.h) \ + $(wildcard include/config/irqsoff/tracer.h) \ + $(wildcard include/config/preempt/tracer.h) \ + $(wildcard include/config/trace/irqflags/support.h) \ + arch/x86/include/asm/irqflags.h \ + arch/x86/include/asm/fpu/types.h \ + arch/x86/include/asm/vmxfeatures.h \ + arch/x86/include/asm/vdso/processor.h \ + include/linux/personality.h \ + include/uapi/linux/personality.h \ + include/linux/err.h \ + include/linux/bottom_half.h \ + arch/x86/include/generated/asm/mmiowb.h \ + include/asm-generic/mmiowb.h \ + $(wildcard include/config/mmiowb.h) \ + include/linux/spinlock_types.h \ + include/linux/lockdep.h \ + $(wildcard include/config/prove/raw/lock/nesting.h) \ + $(wildcard include/config/preempt/lock.h) \ + $(wildcard include/config/lockdep.h) \ + $(wildcard include/config/lock/stat.h) \ + include/linux/rwlock_types.h \ + arch/x86/include/asm/spinlock.h \ + arch/x86/include/asm/qspinlock.h \ + include/asm-generic/qspinlock.h \ + arch/x86/include/asm/qrwlock.h \ + include/asm-generic/qrwlock.h \ + include/linux/rwlock.h \ + $(wildcard include/config/preempt.h) \ + include/linux/spinlock_api_smp.h \ + $(wildcard include/config/inline/spin/lock.h) \ + $(wildcard include/config/inline/spin/lock/bh.h) \ + $(wildcard include/config/inline/spin/lock/irq.h) \ + $(wildcard include/config/inline/spin/lock/irqsave.h) \ + $(wildcard include/config/inline/spin/trylock.h) \ + $(wildcard include/config/inline/spin/trylock/bh.h) \ + $(wildcard include/config/uninline/spin/unlock.h) \ + $(wildcard include/config/inline/spin/unlock/bh.h) \ + $(wildcard include/config/inline/spin/unlock/irq.h) \ + $(wildcard include/config/inline/spin/unlock/irqrestore.h) \ + $(wildcard include/config/generic/lockbreak.h) \ + include/linux/rwlock_api_smp.h \ + $(wildcard include/config/inline/read/lock.h) \ + $(wildcard include/config/inline/write/lock.h) \ + $(wildcard include/config/inline/read/lock/bh.h) \ + $(wildcard include/config/inline/write/lock/bh.h) \ + $(wildcard include/config/inline/read/lock/irq.h) \ + $(wildcard include/config/inline/write/lock/irq.h) \ + $(wildcard include/config/inline/read/lock/irqsave.h) \ + $(wildcard include/config/inline/write/lock/irqsave.h) \ + $(wildcard include/config/inline/read/trylock.h) \ + $(wildcard include/config/inline/write/trylock.h) \ + $(wildcard include/config/inline/read/unlock.h) \ + $(wildcard include/config/inline/write/unlock.h) \ + $(wildcard include/config/inline/read/unlock/bh.h) \ + $(wildcard include/config/inline/write/unlock/bh.h) \ + $(wildcard include/config/inline/read/unlock/irq.h) \ + $(wildcard include/config/inline/write/unlock/irq.h) \ + $(wildcard include/config/inline/read/unlock/irqrestore.h) \ + $(wildcard include/config/inline/write/unlock/irqrestore.h) \ + include/linux/time32.h \ + include/linux/timex.h \ + include/uapi/linux/timex.h \ + include/uapi/linux/param.h \ + arch/x86/include/generated/uapi/asm/param.h \ + include/asm-generic/param.h \ + $(wildcard include/config/hz.h) \ + include/uapi/asm-generic/param.h \ + arch/x86/include/asm/timex.h \ + arch/x86/include/asm/tsc.h \ + $(wildcard include/config/x86/tsc.h) \ + include/vdso/time32.h \ + include/vdso/time.h \ + include/linux/uidgid.h \ + $(wildcard include/config/multiuser.h) \ + $(wildcard include/config/user/ns.h) \ + include/linux/highuid.h \ + include/linux/kmod.h \ + include/linux/umh.h \ + include/linux/gfp.h \ + $(wildcard include/config/highmem.h) \ + $(wildcard include/config/zone/dma.h) \ + $(wildcard include/config/zone/dma32.h) \ + $(wildcard include/config/zone/device.h) \ + $(wildcard include/config/numa.h) \ + $(wildcard include/config/pm/sleep.h) \ + $(wildcard include/config/contig/alloc.h) \ + $(wildcard include/config/cma.h) \ + include/linux/mmdebug.h \ + $(wildcard include/config/debug/vm.h) \ + $(wildcard include/config/debug/vm/pgflags.h) \ + include/linux/mmzone.h \ + $(wildcard include/config/force/max/zoneorder.h) \ + $(wildcard include/config/memory/isolation.h) \ + $(wildcard include/config/shadow/call/stack.h) \ + $(wildcard include/config/zsmalloc.h) \ + $(wildcard include/config/memcg.h) \ + $(wildcard include/config/memory/hotplug.h) \ + $(wildcard include/config/compaction.h) \ + $(wildcard include/config/transparent/hugepage.h) \ + $(wildcard include/config/flat/node/mem/map.h) \ + $(wildcard include/config/page/extension.h) \ + $(wildcard include/config/deferred/struct/page/init.h) \ + $(wildcard include/config/have/memory/present.h) \ + $(wildcard include/config/have/memoryless/nodes.h) \ + $(wildcard include/config/need/multiple/nodes.h) \ + $(wildcard include/config/sparsemem/extreme.h) \ + $(wildcard include/config/memory/hotremove.h) \ + $(wildcard include/config/have/arch/pfn/valid.h) \ + $(wildcard include/config/holes/in/zone.h) \ + $(wildcard include/config/arch/has/holes/memorymodel.h) \ + include/linux/wait.h \ + include/uapi/linux/wait.h \ + include/linux/numa.h \ + $(wildcard include/config/nodes/shift.h) \ + $(wildcard include/config/numa/keep/meminfo.h) \ + include/linux/nodemask.h \ + include/linux/pageblock-flags.h \ + $(wildcard include/config/hugetlb/page.h) \ + $(wildcard include/config/hugetlb/page/size/variable.h) \ + include/linux/page-flags-layout.h \ + $(wildcard include/config/numa/balancing.h) \ + $(wildcard include/config/kasan/sw/tags.h) \ + include/generated/bounds.h \ + include/linux/mm_types.h \ + $(wildcard include/config/have/aligned/struct/page.h) \ + $(wildcard include/config/userfaultfd.h) \ + $(wildcard include/config/swap.h) \ + $(wildcard include/config/have/arch/compat/mmap/bases.h) \ + $(wildcard include/config/membarrier.h) \ + $(wildcard include/config/aio.h) \ + $(wildcard include/config/mmu/notifier.h) \ + $(wildcard include/config/arch/want/batched/unmap/tlb/flush.h) \ + include/linux/mm_types_task.h \ + $(wildcard include/config/split/ptlock/cpus.h) \ + $(wildcard include/config/arch/enable/split/pmd/ptlock.h) \ + arch/x86/include/asm/tlbbatch.h \ + include/linux/auxvec.h \ + include/uapi/linux/auxvec.h \ + arch/x86/include/uapi/asm/auxvec.h \ + include/linux/rbtree.h \ + include/linux/rcupdate.h \ + $(wildcard include/config/preempt/rcu.h) \ + $(wildcard include/config/rcu/stall/common.h) \ + $(wildcard include/config/no/hz/full.h) \ + $(wildcard include/config/rcu/nocb/cpu.h) \ + $(wildcard include/config/tasks/rcu/generic.h) \ + $(wildcard include/config/tasks/rcu.h) \ + $(wildcard include/config/tasks/rcu/trace.h) \ + $(wildcard include/config/tasks/rude/rcu.h) \ + $(wildcard include/config/tree/rcu.h) \ + $(wildcard include/config/tiny/rcu.h) \ + $(wildcard include/config/debug/objects/rcu/head.h) \ + $(wildcard include/config/prove/rcu.h) \ + $(wildcard include/config/rcu/boost.h) \ + $(wildcard include/config/arch/weak/release/acquire.h) \ + include/linux/rcutree.h \ + include/linux/rwsem.h \ + $(wildcard include/config/rwsem/spin/on/owner.h) \ + $(wildcard include/config/debug/rwsems.h) \ + include/linux/osq_lock.h \ + include/linux/completion.h \ + include/linux/swait.h \ + include/linux/uprobes.h \ + $(wildcard include/config/uprobes.h) \ + include/linux/errno.h \ + include/uapi/linux/errno.h \ + arch/x86/include/asm/uprobes.h \ + include/linux/notifier.h \ + include/linux/mutex.h \ + $(wildcard include/config/mutex/spin/on/owner.h) \ + $(wildcard include/config/debug/mutexes.h) \ + include/linux/debug_locks.h \ + $(wildcard include/config/debug/locking/api/selftests.h) \ + include/linux/srcu.h \ + $(wildcard include/config/tiny/srcu.h) \ + $(wildcard include/config/srcu.h) \ + include/linux/workqueue.h \ + $(wildcard include/config/debug/objects/work.h) \ + $(wildcard include/config/freezer.h) \ + $(wildcard include/config/wq/watchdog.h) \ + include/linux/timer.h \ + $(wildcard include/config/debug/objects/timers.h) \ + $(wildcard include/config/no/hz/common.h) \ + include/linux/ktime.h \ + include/linux/jiffies.h \ + include/vdso/jiffies.h \ + include/generated/timeconst.h \ + include/vdso/ktime.h \ + include/linux/timekeeping.h \ + include/linux/timekeeping32.h \ + include/linux/debugobjects.h \ + $(wildcard include/config/debug/objects.h) \ + $(wildcard include/config/debug/objects/free.h) \ + include/linux/rcu_segcblist.h \ + include/linux/srcutree.h \ + include/linux/rcu_node_tree.h \ + $(wildcard include/config/rcu/fanout.h) \ + $(wildcard include/config/rcu/fanout/leaf.h) \ + arch/x86/include/asm/mmu.h \ + $(wildcard include/config/modify/ldt/syscall.h) \ + include/linux/page-flags.h \ + $(wildcard include/config/arch/uses/pg/uncached.h) \ + $(wildcard include/config/memory/failure.h) \ + $(wildcard include/config/idle/page/tracking.h) \ + $(wildcard include/config/thp/swap.h) \ + $(wildcard include/config/ksm.h) \ + include/linux/memory_hotplug.h \ + $(wildcard include/config/arch/has/add/pages.h) \ + $(wildcard include/config/have/arch/nodedata/extension.h) \ + $(wildcard include/config/have/bootmem/info/node.h) \ + arch/x86/include/asm/mmzone.h \ + arch/x86/include/asm/mmzone_64.h \ + arch/x86/include/asm/smp.h \ + $(wildcard include/config/x86/local/apic.h) \ + $(wildcard include/config/x86/io/apic.h) \ + $(wildcard include/config/debug/nmi/selftest.h) \ + arch/x86/include/asm/mpspec.h \ + $(wildcard include/config/eisa.h) \ + $(wildcard include/config/x86/mpparse.h) \ + arch/x86/include/asm/mpspec_def.h \ + arch/x86/include/asm/x86_init.h \ + arch/x86/include/asm/apicdef.h \ + arch/x86/include/asm/apic.h \ + $(wildcard include/config/x86/x2apic.h) \ + arch/x86/include/asm/fixmap.h \ + $(wildcard include/config/provide/ohci1394/dma/init.h) \ + $(wildcard include/config/pci/mmconfig.h) \ + $(wildcard include/config/x86/intel/mid.h) \ + $(wildcard include/config/acpi/apei/ghes.h) \ + $(wildcard include/config/intel/txt.h) \ + arch/x86/include/asm/acpi.h \ + $(wildcard include/config/acpi/apei.h) \ + $(wildcard include/config/acpi.h) \ + $(wildcard include/config/acpi/numa.h) \ + include/acpi/pdc_intel.h \ + arch/x86/include/asm/numa.h \ + $(wildcard include/config/numa/emu.h) \ + arch/x86/include/asm/topology.h \ + $(wildcard include/config/sched/mc/prio.h) \ + include/asm-generic/topology.h \ + arch/x86/include/uapi/asm/vsyscall.h \ + include/asm-generic/fixmap.h \ + arch/x86/include/asm/hardirq.h \ + $(wildcard include/config/kvm/intel.h) \ + $(wildcard include/config/have/kvm.h) \ + $(wildcard include/config/x86/thermal/vector.h) \ + $(wildcard include/config/x86/mce/threshold.h) \ + $(wildcard include/config/x86/mce/amd.h) \ + $(wildcard include/config/x86/hv/callback/vector.h) \ + $(wildcard include/config/hyperv.h) \ + arch/x86/include/asm/io_apic.h \ + arch/x86/include/asm/irq_vectors.h \ + $(wildcard include/config/pci/msi.h) \ + include/linux/topology.h \ + $(wildcard include/config/use/percpu/numa/node/id.h) \ + $(wildcard include/config/sched/smt.h) \ + include/linux/arch_topology.h \ + $(wildcard include/config/generic/arch/topology.h) \ + include/linux/percpu.h \ + $(wildcard include/config/need/per/cpu/embed/first/chunk.h) \ + $(wildcard include/config/need/per/cpu/page/first/chunk.h) \ + include/linux/smp.h \ + $(wildcard include/config/up/late/init.h) \ + include/linux/smp_types.h \ + include/linux/llist.h \ + $(wildcard include/config/arch/have/nmi/safe/cmpxchg.h) \ + include/linux/sysctl.h \ + $(wildcard include/config/sysctl.h) \ + include/uapi/linux/sysctl.h \ + include/linux/elf.h \ + $(wildcard include/config/arch/use/gnu/property.h) \ + $(wildcard include/config/arch/have/elf/prot.h) \ + arch/x86/include/asm/elf.h \ + $(wildcard include/config/x86/x32/abi.h) \ + arch/x86/include/asm/user.h \ + arch/x86/include/asm/user_64.h \ + arch/x86/include/asm/fsgsbase.h \ + arch/x86/include/asm/vdso.h \ + $(wildcard include/config/x86/x32.h) \ + include/uapi/linux/elf.h \ + include/uapi/linux/elf-em.h \ + include/linux/kobject.h \ + $(wildcard include/config/uevent/helper.h) \ + $(wildcard include/config/debug/kobject/release.h) \ + include/linux/sysfs.h \ + include/linux/kernfs.h \ + $(wildcard include/config/kernfs.h) \ + include/linux/idr.h \ + include/linux/radix-tree.h \ + include/linux/xarray.h \ + $(wildcard include/config/xarray/multi.h) \ + include/linux/kconfig.h \ + include/linux/kobject_ns.h \ + include/linux/kref.h \ + include/linux/refcount.h \ + include/linux/moduleparam.h \ + $(wildcard include/config/alpha.h) \ + $(wildcard include/config/ia64.h) \ + $(wildcard include/config/ppc64.h) \ + include/linux/rbtree_latch.h \ + include/linux/error-injection.h \ + include/asm-generic/error-injection.h \ + arch/x86/include/asm/module.h \ + $(wildcard include/config/unwinder/orc.h) \ + include/asm-generic/module.h \ + $(wildcard include/config/have/mod/arch/specific.h) \ + $(wildcard include/config/modules/use/elf/rel.h) \ + $(wildcard include/config/modules/use/elf/rela.h) \ + arch/x86/include/asm/orc_types.h \ + include/linux/build-salt.h \ + $(wildcard include/config/build/salt.h) \ + include/linux/elfnote.h \ + include/linux/vermagic.h \ + include/generated/utsrelease.h \ + arch/x86/include/asm/vermagic.h \ + $(wildcard include/config/m486sx.h) \ + $(wildcard include/config/m486.h) \ + $(wildcard include/config/m586.h) \ + $(wildcard include/config/m586tsc.h) \ + $(wildcard include/config/m586mmx.h) \ + $(wildcard include/config/mcore2.h) \ + $(wildcard include/config/m686.h) \ + $(wildcard include/config/mpentiumii.h) \ + $(wildcard include/config/mpentiumiii.h) \ + $(wildcard include/config/mpentiumm.h) \ + $(wildcard include/config/mpentium4.h) \ + $(wildcard include/config/mk6.h) \ + $(wildcard include/config/mk8.h) \ + $(wildcard include/config/melan.h) \ + $(wildcard include/config/mcrusoe.h) \ + $(wildcard include/config/mefficeon.h) \ + $(wildcard include/config/mwinchipc6.h) \ + $(wildcard include/config/mwinchip3d.h) \ + $(wildcard include/config/mcyrixiii.h) \ + $(wildcard include/config/mviac3/2.h) \ + $(wildcard include/config/mviac7.h) \ + $(wildcard include/config/mgeodegx1.h) \ + $(wildcard include/config/mgeode/lx.h) \ + +/home/chao/code/module/hellow/hello.mod.o: $(deps_/home/chao/code/module/hellow/hello.mod.o) + +$(deps_/home/chao/code/module/hellow/hello.mod.o): diff --git a/hellow/.hello.o.cmd b/hellow/.hello.o.cmd new file mode 100644 index 0000000..4377a8b --- /dev/null +++ b/hellow/.hello.o.cmd @@ -0,0 +1,665 @@ +cmd_/home/chao/code/module/hellow/hello.o := gcc -Wp,-MMD,/home/chao/code/module/hellow/.hello.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/9/include -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -Iubuntu/include -include ./include/linux/compiler_types.h -D__KERNEL__ -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=none -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -DCONFIG_X86_X32_ABI -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -fno-jump-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-format-truncation -Wno-format-overflow -Wno-address-of-packed-member -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fstack-protector-strong -Wno-unused-but-set-variable -Wimplicit-fallthrough -Wno-unused-const-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-var-tracking-assignments -pg -mrecord-mcount -mfentry -DCC_USING_FENTRY -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-stringop-truncation -Wno-array-bounds -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized -fno-strict-overflow -fno-merge-all-constants -fmerge-constants -fno-stack-check -fconserve-stack -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -fmacro-prefix-map=./= -Wno-packed-not-aligned -DMODULE -DKBUILD_BASENAME='"hello"' -DKBUILD_MODNAME='"hello"' -c -o /home/chao/code/module/hellow/hello.o /home/chao/code/module/hellow/hello.c + +source_/home/chao/code/module/hellow/hello.o := /home/chao/code/module/hellow/hello.c + +deps_/home/chao/code/module/hellow/hello.o := \ + include/linux/kconfig.h \ + $(wildcard include/config/cc/version/text.h) \ + $(wildcard include/config/cpu/big/endian.h) \ + $(wildcard include/config/booger.h) \ + $(wildcard include/config/foo.h) \ + include/linux/compiler_types.h \ + $(wildcard include/config/have/arch/compiler/h.h) \ + $(wildcard include/config/enable/must/check.h) \ + $(wildcard include/config/cc/has/asm/inline.h) \ + include/linux/compiler_attributes.h \ + include/linux/compiler-gcc.h \ + $(wildcard include/config/arm64.h) \ + $(wildcard include/config/retpoline.h) \ + $(wildcard include/config/arch/use/builtin/bswap.h) \ + include/linux/init.h \ + $(wildcard include/config/have/arch/prel32/relocations.h) \ + $(wildcard include/config/strict/kernel/rwx.h) \ + $(wildcard include/config/strict/module/rwx.h) \ + include/linux/compiler.h \ + $(wildcard include/config/trace/branch/profiling.h) \ + $(wildcard include/config/profile/all/branches.h) \ + $(wildcard include/config/stack/validation.h) \ + $(wildcard include/config/debug/entry.h) \ + include/linux/compiler_types.h \ + arch/x86/include/asm/barrier.h \ + $(wildcard include/config/x86/32.h) \ + arch/x86/include/asm/alternative.h \ + $(wildcard include/config/smp.h) \ + include/linux/types.h \ + $(wildcard include/config/have/uid16.h) \ + $(wildcard include/config/uid16.h) \ + $(wildcard include/config/arch/dma/addr/t/64bit.h) \ + $(wildcard include/config/phys/addr/t/64bit.h) \ + $(wildcard include/config/64bit.h) \ + include/uapi/linux/types.h \ + arch/x86/include/generated/uapi/asm/types.h \ + include/uapi/asm-generic/types.h \ + include/asm-generic/int-ll64.h \ + include/uapi/asm-generic/int-ll64.h \ + arch/x86/include/uapi/asm/bitsperlong.h \ + include/asm-generic/bitsperlong.h \ + include/uapi/asm-generic/bitsperlong.h \ + include/uapi/linux/posix_types.h \ + include/linux/stddef.h \ + include/uapi/linux/stddef.h \ + arch/x86/include/asm/posix_types.h \ + arch/x86/include/uapi/asm/posix_types_64.h \ + include/uapi/asm-generic/posix_types.h \ + include/linux/stringify.h \ + arch/x86/include/asm/asm.h \ + arch/x86/include/asm/nops.h \ + $(wildcard include/config/mk7.h) \ + $(wildcard include/config/x86/p6/nop.h) \ + $(wildcard include/config/x86/64.h) \ + include/asm-generic/barrier.h \ + include/linux/kasan-checks.h \ + $(wildcard include/config/kasan.h) \ + include/linux/kcsan-checks.h \ + $(wildcard include/config/kcsan.h) \ + $(wildcard include/config/kcsan/ignore/atomics.h) \ + include/linux/module.h \ + $(wildcard include/config/modules.h) \ + $(wildcard include/config/sysfs.h) \ + $(wildcard include/config/modules/tree/lookup.h) \ + $(wildcard include/config/livepatch.h) \ + $(wildcard include/config/unused/symbols.h) \ + $(wildcard include/config/module/sig.h) \ + $(wildcard include/config/generic/bug.h) \ + $(wildcard include/config/kallsyms.h) \ + $(wildcard include/config/tracepoints.h) \ + $(wildcard include/config/tree/srcu.h) \ + $(wildcard include/config/bpf/events.h) \ + $(wildcard include/config/jump/label.h) \ + $(wildcard include/config/tracing.h) \ + $(wildcard include/config/event/tracing.h) \ + $(wildcard include/config/ftrace/mcount/record.h) \ + $(wildcard include/config/kprobes.h) \ + $(wildcard include/config/module/unload.h) \ + $(wildcard include/config/constructors.h) \ + $(wildcard include/config/function/error/injection.h) \ + include/linux/list.h \ + $(wildcard include/config/debug/list.h) \ + include/linux/poison.h \ + $(wildcard include/config/illegal/pointer/value.h) \ + $(wildcard include/config/page/poisoning/zero.h) \ + include/linux/const.h \ + include/vdso/const.h \ + include/uapi/linux/const.h \ + include/linux/kernel.h \ + $(wildcard include/config/preempt/voluntary.h) \ + $(wildcard include/config/debug/atomic/sleep.h) \ + $(wildcard include/config/preempt/rt.h) \ + $(wildcard include/config/mmu.h) \ + $(wildcard include/config/prove/locking.h) \ + $(wildcard include/config/panic/timeout.h) \ + include/linux/limits.h \ + include/uapi/linux/limits.h \ + include/vdso/limits.h \ + include/linux/linkage.h \ + $(wildcard include/config/arch/use/sym/annotations.h) \ + include/linux/export.h \ + $(wildcard include/config/modversions.h) \ + $(wildcard include/config/module/rel/crcs.h) \ + $(wildcard include/config/trim/unused/ksyms.h) \ + arch/x86/include/asm/linkage.h \ + $(wildcard include/config/x86/alignment/16.h) \ + include/linux/bitops.h \ + include/linux/bits.h \ + include/vdso/bits.h \ + include/linux/build_bug.h \ + arch/x86/include/asm/bitops.h \ + $(wildcard include/config/x86/cmov.h) \ + arch/x86/include/asm/rmwcc.h \ + $(wildcard include/config/cc/has/asm/goto.h) \ + include/asm-generic/bitops/find.h \ + $(wildcard include/config/generic/find/first/bit.h) \ + include/asm-generic/bitops/sched.h \ + arch/x86/include/asm/arch_hweight.h \ + arch/x86/include/asm/cpufeatures.h \ + arch/x86/include/asm/required-features.h \ + $(wildcard include/config/x86/minimum/cpu/family.h) \ + $(wildcard include/config/math/emulation.h) \ + $(wildcard include/config/x86/pae.h) \ + $(wildcard include/config/x86/cmpxchg64.h) \ + $(wildcard include/config/x86/use/3dnow.h) \ + $(wildcard include/config/matom.h) \ + $(wildcard include/config/paravirt.h) \ + arch/x86/include/asm/disabled-features.h \ + $(wildcard include/config/x86/smap.h) \ + $(wildcard include/config/x86/umip.h) \ + $(wildcard include/config/x86/intel/memory/protection/keys.h) \ + $(wildcard include/config/x86/5level.h) \ + $(wildcard include/config/page/table/isolation.h) \ + include/asm-generic/bitops/const_hweight.h \ + include/asm-generic/bitops/instrumented-atomic.h \ + include/linux/instrumented.h \ + include/asm-generic/bitops/instrumented-non-atomic.h \ + include/asm-generic/bitops/instrumented-lock.h \ + include/asm-generic/bitops/le.h \ + arch/x86/include/uapi/asm/byteorder.h \ + include/linux/byteorder/little_endian.h \ + include/uapi/linux/byteorder/little_endian.h \ + include/linux/swab.h \ + include/uapi/linux/swab.h \ + arch/x86/include/uapi/asm/swab.h \ + include/linux/byteorder/generic.h \ + include/asm-generic/bitops/ext2-atomic-setbit.h \ + include/linux/log2.h \ + $(wildcard include/config/arch/has/ilog2/u32.h) \ + $(wildcard include/config/arch/has/ilog2/u64.h) \ + include/linux/typecheck.h \ + include/linux/printk.h \ + $(wildcard include/config/message/loglevel/default.h) \ + $(wildcard include/config/console/loglevel/default.h) \ + $(wildcard include/config/console/loglevel/quiet.h) \ + $(wildcard include/config/early/printk.h) \ + $(wildcard include/config/printk/nmi.h) \ + $(wildcard include/config/printk.h) \ + $(wildcard include/config/kmsg/ids.h) \ + $(wildcard include/config/dynamic/debug.h) \ + $(wildcard include/config/dynamic/debug/core.h) \ + include/linux/kern_levels.h \ + include/linux/cache.h \ + $(wildcard include/config/arch/has/cache/line/size.h) \ + include/uapi/linux/kernel.h \ + include/uapi/linux/sysinfo.h \ + arch/x86/include/asm/cache.h \ + $(wildcard include/config/x86/l1/cache/shift.h) \ + $(wildcard include/config/x86/internode/cache/shift.h) \ + $(wildcard include/config/x86/vsmp.h) \ + include/linux/dynamic_debug.h \ + include/linux/jump_label.h \ + $(wildcard include/config/have/arch/jump/label/relative.h) \ + arch/x86/include/asm/jump_label.h \ + arch/x86/include/asm/div64.h \ + include/asm-generic/div64.h \ + include/linux/stat.h \ + arch/x86/include/uapi/asm/stat.h \ + include/uapi/linux/stat.h \ + include/linux/time.h \ + $(wildcard include/config/arch/uses/gettimeoffset.h) \ + $(wildcard include/config/posix/timers.h) \ + include/linux/seqlock.h \ + $(wildcard include/config/debug/lock/alloc.h) \ + include/linux/spinlock.h \ + $(wildcard include/config/debug/spinlock.h) \ + $(wildcard include/config/preemption.h) \ + include/linux/preempt.h \ + $(wildcard include/config/preempt/count.h) \ + $(wildcard include/config/debug/preempt.h) \ + $(wildcard include/config/trace/preempt/toggle.h) \ + $(wildcard include/config/preempt/notifiers.h) \ + arch/x86/include/asm/preempt.h \ + arch/x86/include/asm/percpu.h \ + $(wildcard include/config/x86/64/smp.h) \ + include/asm-generic/percpu.h \ + $(wildcard include/config/have/setup/per/cpu/area.h) \ + include/linux/threads.h \ + $(wildcard include/config/nr/cpus.h) \ + $(wildcard include/config/base/small.h) \ + include/linux/percpu-defs.h \ + $(wildcard include/config/debug/force/weak/per/cpu.h) \ + $(wildcard include/config/amd/mem/encrypt.h) \ + include/linux/thread_info.h \ + $(wildcard include/config/thread/info/in/task.h) \ + $(wildcard include/config/have/arch/within/stack/frames.h) \ + $(wildcard include/config/hardened/usercopy.h) \ + include/linux/bug.h \ + $(wildcard include/config/bug/on/data/corruption.h) \ + arch/x86/include/asm/bug.h \ + $(wildcard include/config/debug/bugverbose.h) \ + include/asm-generic/bug.h \ + $(wildcard include/config/bug.h) \ + $(wildcard include/config/generic/bug/relative/pointers.h) \ + include/linux/restart_block.h \ + include/linux/time64.h \ + include/linux/math64.h \ + $(wildcard include/config/arch/supports/int128.h) \ + include/vdso/math64.h \ + include/vdso/time64.h \ + include/uapi/linux/time.h \ + include/uapi/linux/time_types.h \ + arch/x86/include/asm/current.h \ + arch/x86/include/asm/thread_info.h \ + $(wildcard include/config/vm86.h) \ + $(wildcard include/config/x86/iopl/ioperm.h) \ + $(wildcard include/config/frame/pointer.h) \ + $(wildcard include/config/compat.h) \ + $(wildcard include/config/ia32/emulation.h) \ + arch/x86/include/asm/page.h \ + arch/x86/include/asm/page_types.h \ + $(wildcard include/config/physical/start.h) \ + $(wildcard include/config/physical/align.h) \ + $(wildcard include/config/dynamic/physical/mask.h) \ + include/linux/mem_encrypt.h \ + $(wildcard include/config/arch/has/mem/encrypt.h) \ + arch/x86/include/asm/mem_encrypt.h \ + arch/x86/include/uapi/asm/bootparam.h \ + include/linux/screen_info.h \ + include/uapi/linux/screen_info.h \ + include/linux/apm_bios.h \ + include/uapi/linux/apm_bios.h \ + include/uapi/linux/ioctl.h \ + arch/x86/include/generated/uapi/asm/ioctl.h \ + include/asm-generic/ioctl.h \ + include/uapi/asm-generic/ioctl.h \ + include/linux/edd.h \ + include/uapi/linux/edd.h \ + arch/x86/include/asm/ist.h \ + arch/x86/include/uapi/asm/ist.h \ + include/video/edid.h \ + $(wildcard include/config/x86.h) \ + include/uapi/video/edid.h \ + arch/x86/include/asm/page_64_types.h \ + $(wildcard include/config/dynamic/memory/layout.h) \ + $(wildcard include/config/randomize/base.h) \ + arch/x86/include/asm/kaslr.h \ + $(wildcard include/config/randomize/memory.h) \ + arch/x86/include/asm/page_64.h \ + $(wildcard include/config/debug/virtual.h) \ + $(wildcard include/config/flatmem.h) \ + $(wildcard include/config/x86/vsyscall/emulation.h) \ + include/linux/range.h \ + include/asm-generic/memory_model.h \ + $(wildcard include/config/discontigmem.h) \ + $(wildcard include/config/sparsemem/vmemmap.h) \ + $(wildcard include/config/sparsemem.h) \ + include/linux/pfn.h \ + include/asm-generic/getorder.h \ + arch/x86/include/asm/cpufeature.h \ + $(wildcard include/config/x86/feature/names.h) \ + arch/x86/include/asm/processor.h \ + $(wildcard include/config/x86/vmx/feature/names.h) \ + $(wildcard include/config/kvm.h) \ + $(wildcard include/config/stackprotector.h) \ + $(wildcard include/config/paravirt/xxl.h) \ + $(wildcard include/config/x86/debugctlmsr.h) \ + $(wildcard include/config/cpu/sup/amd.h) \ + $(wildcard include/config/xen.h) \ + arch/x86/include/asm/processor-flags.h \ + arch/x86/include/uapi/asm/processor-flags.h \ + arch/x86/include/asm/math_emu.h \ + arch/x86/include/asm/ptrace.h \ + arch/x86/include/asm/segment.h \ + $(wildcard include/config/xen/pv.h) \ + $(wildcard include/config/x86/32/lazy/gs.h) \ + arch/x86/include/uapi/asm/ptrace.h \ + arch/x86/include/uapi/asm/ptrace-abi.h \ + arch/x86/include/asm/paravirt_types.h \ + $(wildcard include/config/pgtable/levels.h) \ + $(wildcard include/config/paravirt/debug.h) \ + arch/x86/include/asm/desc_defs.h \ + arch/x86/include/asm/kmap_types.h \ + $(wildcard include/config/debug/highmem.h) \ + include/asm-generic/kmap_types.h \ + arch/x86/include/asm/pgtable_types.h \ + $(wildcard include/config/mem/soft/dirty.h) \ + $(wildcard include/config/have/arch/userfaultfd/wp.h) \ + $(wildcard include/config/proc/fs.h) \ + arch/x86/include/asm/pgtable_64_types.h \ + arch/x86/include/asm/sparsemem.h \ + arch/x86/include/asm/nospec-branch.h \ + include/linux/static_key.h \ + include/linux/frame.h \ + arch/x86/include/asm/alternative-asm.h \ + arch/x86/include/asm/msr-index.h \ + arch/x86/include/asm/unwind_hints.h \ + arch/x86/include/asm/orc_types.h \ + arch/x86/include/asm/spinlock_types.h \ + include/asm-generic/qspinlock_types.h \ + include/asm-generic/qrwlock_types.h \ + arch/x86/include/uapi/asm/sigcontext.h \ + arch/x86/include/asm/msr.h \ + arch/x86/include/asm/msr-index.h \ + arch/x86/include/generated/uapi/asm/errno.h \ + include/uapi/asm-generic/errno.h \ + include/uapi/asm-generic/errno-base.h \ + arch/x86/include/asm/cpumask.h \ + include/linux/cpumask.h \ + $(wildcard include/config/cpumask/offstack.h) \ + $(wildcard include/config/hotplug/cpu.h) \ + $(wildcard include/config/debug/per/cpu/maps.h) \ + include/linux/bitmap.h \ + include/linux/string.h \ + $(wildcard include/config/binary/printf.h) \ + $(wildcard include/config/fortify/source.h) \ + include/uapi/linux/string.h \ + arch/x86/include/asm/string.h \ + arch/x86/include/asm/string_64.h \ + $(wildcard include/config/arch/has/uaccess/flushcache.h) \ + include/linux/atomic.h \ + arch/x86/include/asm/atomic.h \ + arch/x86/include/asm/cmpxchg.h \ + arch/x86/include/asm/cmpxchg_64.h \ + arch/x86/include/asm/atomic64_64.h \ + include/linux/atomic-arch-fallback.h \ + $(wildcard include/config/generic/atomic64.h) \ + include/asm-generic/atomic-instrumented.h \ + include/asm-generic/atomic-long.h \ + arch/x86/include/uapi/asm/msr.h \ + include/linux/tracepoint-defs.h \ + arch/x86/include/asm/paravirt.h \ + $(wildcard include/config/paravirt/spinlocks.h) \ + arch/x86/include/asm/frame.h \ + arch/x86/include/asm/special_insns.h \ + include/linux/irqflags.h \ + $(wildcard include/config/trace/irqflags.h) \ + $(wildcard include/config/irqsoff/tracer.h) \ + $(wildcard include/config/preempt/tracer.h) \ + $(wildcard include/config/trace/irqflags/support.h) \ + arch/x86/include/asm/irqflags.h \ + arch/x86/include/asm/fpu/types.h \ + arch/x86/include/asm/vmxfeatures.h \ + arch/x86/include/asm/vdso/processor.h \ + include/linux/personality.h \ + include/uapi/linux/personality.h \ + include/linux/err.h \ + include/linux/bottom_half.h \ + arch/x86/include/generated/asm/mmiowb.h \ + include/asm-generic/mmiowb.h \ + $(wildcard include/config/mmiowb.h) \ + include/linux/spinlock_types.h \ + include/linux/lockdep.h \ + $(wildcard include/config/prove/raw/lock/nesting.h) \ + $(wildcard include/config/preempt/lock.h) \ + $(wildcard include/config/lockdep.h) \ + $(wildcard include/config/lock/stat.h) \ + include/linux/rwlock_types.h \ + arch/x86/include/asm/spinlock.h \ + arch/x86/include/asm/qspinlock.h \ + include/asm-generic/qspinlock.h \ + arch/x86/include/asm/qrwlock.h \ + include/asm-generic/qrwlock.h \ + include/linux/rwlock.h \ + $(wildcard include/config/preempt.h) \ + include/linux/spinlock_api_smp.h \ + $(wildcard include/config/inline/spin/lock.h) \ + $(wildcard include/config/inline/spin/lock/bh.h) \ + $(wildcard include/config/inline/spin/lock/irq.h) \ + $(wildcard include/config/inline/spin/lock/irqsave.h) \ + $(wildcard include/config/inline/spin/trylock.h) \ + $(wildcard include/config/inline/spin/trylock/bh.h) \ + $(wildcard include/config/uninline/spin/unlock.h) \ + $(wildcard include/config/inline/spin/unlock/bh.h) \ + $(wildcard include/config/inline/spin/unlock/irq.h) \ + $(wildcard include/config/inline/spin/unlock/irqrestore.h) \ + $(wildcard include/config/generic/lockbreak.h) \ + include/linux/rwlock_api_smp.h \ + $(wildcard include/config/inline/read/lock.h) \ + $(wildcard include/config/inline/write/lock.h) \ + $(wildcard include/config/inline/read/lock/bh.h) \ + $(wildcard include/config/inline/write/lock/bh.h) \ + $(wildcard include/config/inline/read/lock/irq.h) \ + $(wildcard include/config/inline/write/lock/irq.h) \ + $(wildcard include/config/inline/read/lock/irqsave.h) \ + $(wildcard include/config/inline/write/lock/irqsave.h) \ + $(wildcard include/config/inline/read/trylock.h) \ + $(wildcard include/config/inline/write/trylock.h) \ + $(wildcard include/config/inline/read/unlock.h) \ + $(wildcard include/config/inline/write/unlock.h) \ + $(wildcard include/config/inline/read/unlock/bh.h) \ + $(wildcard include/config/inline/write/unlock/bh.h) \ + $(wildcard include/config/inline/read/unlock/irq.h) \ + $(wildcard include/config/inline/write/unlock/irq.h) \ + $(wildcard include/config/inline/read/unlock/irqrestore.h) \ + $(wildcard include/config/inline/write/unlock/irqrestore.h) \ + include/linux/time32.h \ + include/linux/timex.h \ + include/uapi/linux/timex.h \ + include/uapi/linux/param.h \ + arch/x86/include/generated/uapi/asm/param.h \ + include/asm-generic/param.h \ + $(wildcard include/config/hz.h) \ + include/uapi/asm-generic/param.h \ + arch/x86/include/asm/timex.h \ + arch/x86/include/asm/tsc.h \ + $(wildcard include/config/x86/tsc.h) \ + include/vdso/time32.h \ + include/vdso/time.h \ + include/linux/uidgid.h \ + $(wildcard include/config/multiuser.h) \ + $(wildcard include/config/user/ns.h) \ + include/linux/highuid.h \ + include/linux/kmod.h \ + include/linux/umh.h \ + include/linux/gfp.h \ + $(wildcard include/config/highmem.h) \ + $(wildcard include/config/zone/dma.h) \ + $(wildcard include/config/zone/dma32.h) \ + $(wildcard include/config/zone/device.h) \ + $(wildcard include/config/numa.h) \ + $(wildcard include/config/pm/sleep.h) \ + $(wildcard include/config/contig/alloc.h) \ + $(wildcard include/config/cma.h) \ + include/linux/mmdebug.h \ + $(wildcard include/config/debug/vm.h) \ + $(wildcard include/config/debug/vm/pgflags.h) \ + include/linux/mmzone.h \ + $(wildcard include/config/force/max/zoneorder.h) \ + $(wildcard include/config/memory/isolation.h) \ + $(wildcard include/config/shadow/call/stack.h) \ + $(wildcard include/config/zsmalloc.h) \ + $(wildcard include/config/memcg.h) \ + $(wildcard include/config/memory/hotplug.h) \ + $(wildcard include/config/compaction.h) \ + $(wildcard include/config/transparent/hugepage.h) \ + $(wildcard include/config/flat/node/mem/map.h) \ + $(wildcard include/config/page/extension.h) \ + $(wildcard include/config/deferred/struct/page/init.h) \ + $(wildcard include/config/have/memory/present.h) \ + $(wildcard include/config/have/memoryless/nodes.h) \ + $(wildcard include/config/need/multiple/nodes.h) \ + $(wildcard include/config/sparsemem/extreme.h) \ + $(wildcard include/config/memory/hotremove.h) \ + $(wildcard include/config/have/arch/pfn/valid.h) \ + $(wildcard include/config/holes/in/zone.h) \ + $(wildcard include/config/arch/has/holes/memorymodel.h) \ + include/linux/wait.h \ + include/uapi/linux/wait.h \ + include/linux/numa.h \ + $(wildcard include/config/nodes/shift.h) \ + $(wildcard include/config/numa/keep/meminfo.h) \ + include/linux/nodemask.h \ + include/linux/pageblock-flags.h \ + $(wildcard include/config/hugetlb/page.h) \ + $(wildcard include/config/hugetlb/page/size/variable.h) \ + include/linux/page-flags-layout.h \ + $(wildcard include/config/numa/balancing.h) \ + $(wildcard include/config/kasan/sw/tags.h) \ + include/generated/bounds.h \ + include/linux/mm_types.h \ + $(wildcard include/config/have/aligned/struct/page.h) \ + $(wildcard include/config/userfaultfd.h) \ + $(wildcard include/config/swap.h) \ + $(wildcard include/config/have/arch/compat/mmap/bases.h) \ + $(wildcard include/config/membarrier.h) \ + $(wildcard include/config/aio.h) \ + $(wildcard include/config/mmu/notifier.h) \ + $(wildcard include/config/arch/want/batched/unmap/tlb/flush.h) \ + include/linux/mm_types_task.h \ + $(wildcard include/config/split/ptlock/cpus.h) \ + $(wildcard include/config/arch/enable/split/pmd/ptlock.h) \ + arch/x86/include/asm/tlbbatch.h \ + include/linux/auxvec.h \ + include/uapi/linux/auxvec.h \ + arch/x86/include/uapi/asm/auxvec.h \ + include/linux/rbtree.h \ + include/linux/rcupdate.h \ + $(wildcard include/config/preempt/rcu.h) \ + $(wildcard include/config/rcu/stall/common.h) \ + $(wildcard include/config/no/hz/full.h) \ + $(wildcard include/config/rcu/nocb/cpu.h) \ + $(wildcard include/config/tasks/rcu/generic.h) \ + $(wildcard include/config/tasks/rcu.h) \ + $(wildcard include/config/tasks/rcu/trace.h) \ + $(wildcard include/config/tasks/rude/rcu.h) \ + $(wildcard include/config/tree/rcu.h) \ + $(wildcard include/config/tiny/rcu.h) \ + $(wildcard include/config/debug/objects/rcu/head.h) \ + $(wildcard include/config/prove/rcu.h) \ + $(wildcard include/config/rcu/boost.h) \ + $(wildcard include/config/arch/weak/release/acquire.h) \ + include/linux/rcutree.h \ + include/linux/rwsem.h \ + $(wildcard include/config/rwsem/spin/on/owner.h) \ + $(wildcard include/config/debug/rwsems.h) \ + include/linux/osq_lock.h \ + include/linux/completion.h \ + include/linux/swait.h \ + include/linux/uprobes.h \ + $(wildcard include/config/uprobes.h) \ + include/linux/errno.h \ + include/uapi/linux/errno.h \ + arch/x86/include/asm/uprobes.h \ + include/linux/notifier.h \ + include/linux/mutex.h \ + $(wildcard include/config/mutex/spin/on/owner.h) \ + $(wildcard include/config/debug/mutexes.h) \ + include/linux/debug_locks.h \ + $(wildcard include/config/debug/locking/api/selftests.h) \ + include/linux/srcu.h \ + $(wildcard include/config/tiny/srcu.h) \ + $(wildcard include/config/srcu.h) \ + include/linux/workqueue.h \ + $(wildcard include/config/debug/objects/work.h) \ + $(wildcard include/config/freezer.h) \ + $(wildcard include/config/wq/watchdog.h) \ + include/linux/timer.h \ + $(wildcard include/config/debug/objects/timers.h) \ + $(wildcard include/config/no/hz/common.h) \ + include/linux/ktime.h \ + include/linux/jiffies.h \ + include/vdso/jiffies.h \ + include/generated/timeconst.h \ + include/vdso/ktime.h \ + include/linux/timekeeping.h \ + include/linux/timekeeping32.h \ + include/linux/debugobjects.h \ + $(wildcard include/config/debug/objects.h) \ + $(wildcard include/config/debug/objects/free.h) \ + include/linux/rcu_segcblist.h \ + include/linux/srcutree.h \ + include/linux/rcu_node_tree.h \ + $(wildcard include/config/rcu/fanout.h) \ + $(wildcard include/config/rcu/fanout/leaf.h) \ + arch/x86/include/asm/mmu.h \ + $(wildcard include/config/modify/ldt/syscall.h) \ + include/linux/page-flags.h \ + $(wildcard include/config/arch/uses/pg/uncached.h) \ + $(wildcard include/config/memory/failure.h) \ + $(wildcard include/config/idle/page/tracking.h) \ + $(wildcard include/config/thp/swap.h) \ + $(wildcard include/config/ksm.h) \ + include/linux/memory_hotplug.h \ + $(wildcard include/config/arch/has/add/pages.h) \ + $(wildcard include/config/have/arch/nodedata/extension.h) \ + $(wildcard include/config/have/bootmem/info/node.h) \ + arch/x86/include/asm/mmzone.h \ + arch/x86/include/asm/mmzone_64.h \ + arch/x86/include/asm/smp.h \ + $(wildcard include/config/x86/local/apic.h) \ + $(wildcard include/config/x86/io/apic.h) \ + $(wildcard include/config/debug/nmi/selftest.h) \ + arch/x86/include/asm/mpspec.h \ + $(wildcard include/config/eisa.h) \ + $(wildcard include/config/x86/mpparse.h) \ + arch/x86/include/asm/mpspec_def.h \ + arch/x86/include/asm/x86_init.h \ + arch/x86/include/asm/apicdef.h \ + arch/x86/include/asm/apic.h \ + $(wildcard include/config/x86/x2apic.h) \ + arch/x86/include/asm/fixmap.h \ + $(wildcard include/config/provide/ohci1394/dma/init.h) \ + $(wildcard include/config/pci/mmconfig.h) \ + $(wildcard include/config/x86/intel/mid.h) \ + $(wildcard include/config/acpi/apei/ghes.h) \ + $(wildcard include/config/intel/txt.h) \ + arch/x86/include/asm/acpi.h \ + $(wildcard include/config/acpi/apei.h) \ + $(wildcard include/config/acpi.h) \ + $(wildcard include/config/acpi/numa.h) \ + include/acpi/pdc_intel.h \ + arch/x86/include/asm/numa.h \ + $(wildcard include/config/numa/emu.h) \ + arch/x86/include/asm/topology.h \ + $(wildcard include/config/sched/mc/prio.h) \ + include/asm-generic/topology.h \ + arch/x86/include/uapi/asm/vsyscall.h \ + include/asm-generic/fixmap.h \ + arch/x86/include/asm/hardirq.h \ + $(wildcard include/config/kvm/intel.h) \ + $(wildcard include/config/have/kvm.h) \ + $(wildcard include/config/x86/thermal/vector.h) \ + $(wildcard include/config/x86/mce/threshold.h) \ + $(wildcard include/config/x86/mce/amd.h) \ + $(wildcard include/config/x86/hv/callback/vector.h) \ + $(wildcard include/config/hyperv.h) \ + arch/x86/include/asm/io_apic.h \ + arch/x86/include/asm/irq_vectors.h \ + $(wildcard include/config/pci/msi.h) \ + include/linux/topology.h \ + $(wildcard include/config/use/percpu/numa/node/id.h) \ + $(wildcard include/config/sched/smt.h) \ + include/linux/arch_topology.h \ + $(wildcard include/config/generic/arch/topology.h) \ + include/linux/percpu.h \ + $(wildcard include/config/need/per/cpu/embed/first/chunk.h) \ + $(wildcard include/config/need/per/cpu/page/first/chunk.h) \ + include/linux/smp.h \ + $(wildcard include/config/up/late/init.h) \ + include/linux/smp_types.h \ + include/linux/llist.h \ + $(wildcard include/config/arch/have/nmi/safe/cmpxchg.h) \ + include/linux/sysctl.h \ + $(wildcard include/config/sysctl.h) \ + include/uapi/linux/sysctl.h \ + include/linux/elf.h \ + $(wildcard include/config/arch/use/gnu/property.h) \ + $(wildcard include/config/arch/have/elf/prot.h) \ + arch/x86/include/asm/elf.h \ + $(wildcard include/config/x86/x32/abi.h) \ + arch/x86/include/asm/user.h \ + arch/x86/include/asm/user_64.h \ + arch/x86/include/asm/fsgsbase.h \ + arch/x86/include/asm/vdso.h \ + $(wildcard include/config/x86/x32.h) \ + include/uapi/linux/elf.h \ + include/uapi/linux/elf-em.h \ + include/linux/kobject.h \ + $(wildcard include/config/uevent/helper.h) \ + $(wildcard include/config/debug/kobject/release.h) \ + include/linux/sysfs.h \ + include/linux/kernfs.h \ + $(wildcard include/config/kernfs.h) \ + include/linux/idr.h \ + include/linux/radix-tree.h \ + include/linux/xarray.h \ + $(wildcard include/config/xarray/multi.h) \ + include/linux/kconfig.h \ + include/linux/kobject_ns.h \ + include/linux/kref.h \ + include/linux/refcount.h \ + include/linux/moduleparam.h \ + $(wildcard include/config/alpha.h) \ + $(wildcard include/config/ia64.h) \ + $(wildcard include/config/ppc64.h) \ + include/linux/rbtree_latch.h \ + include/linux/error-injection.h \ + include/asm-generic/error-injection.h \ + arch/x86/include/asm/module.h \ + $(wildcard include/config/unwinder/orc.h) \ + include/asm-generic/module.h \ + $(wildcard include/config/have/mod/arch/specific.h) \ + $(wildcard include/config/modules/use/elf/rel.h) \ + $(wildcard include/config/modules/use/elf/rela.h) \ + arch/x86/include/asm/orc_types.h \ + +/home/chao/code/module/hellow/hello.o: $(deps_/home/chao/code/module/hellow/hello.o) + +$(deps_/home/chao/code/module/hellow/hello.o): diff --git a/hellow/.modules.order.cmd b/hellow/.modules.order.cmd new file mode 100644 index 0000000..a2b6d9a --- /dev/null +++ b/hellow/.modules.order.cmd @@ -0,0 +1 @@ +cmd_/home/chao/code/module/hellow/modules.order := { echo /home/chao/code/module/hellow/hello.ko; :; } | awk '!x[$$0]++' - > /home/chao/code/module/hellow/modules.order diff --git a/hellow/Makefile b/hellow/Makefile new file mode 100644 index 0000000..717367c --- /dev/null +++ b/hellow/Makefile @@ -0,0 +1,14 @@ +ifneq ($(KERNELRELEASE),) + obj-m := hello.o +else + KERNELDIR ?= /lib/modules/$(shell uname -r)/build + PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +endif + +clean: + rm -r ./hello.o + rm -r ./hello.ko \ No newline at end of file diff --git a/hellow/Module.symvers b/hellow/Module.symvers new file mode 100644 index 0000000..e69de29 diff --git a/hellow/hello.c b/hellow/hello.c new file mode 100644 index 0000000..1123ed2 --- /dev/null +++ b/hellow/hello.c @@ -0,0 +1,18 @@ +#include +#include +MODULE_LICENSE("Dual BSD/GPL"); + +static int hello_init(void) +{ + printk(KERN_ALERT "Hello, World!\n"); + return 0; +} + +static void hello_exit(void) +{ + printk(KERN_ALERT "Goodbye, Cruel World!\n"); + return; +} + +module_init(hello_init); +module_exit(hello_exit); \ No newline at end of file diff --git a/hellow/hello.mod b/hellow/hello.mod new file mode 100644 index 0000000..804235f --- /dev/null +++ b/hellow/hello.mod @@ -0,0 +1,2 @@ +/home/chao/code/module/hellow/hello.o + diff --git a/hellow/hello.mod.c b/hellow/hello.mod.c new file mode 100644 index 0000000..c60ce66 --- /dev/null +++ b/hellow/hello.mod.c @@ -0,0 +1,36 @@ +#include +#define INCLUDE_VERMAGIC +#include +#include +#include + +BUILD_SALT; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(.gnu.linkonce.this_module) = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + +static const struct modversion_info ____versions[] +__used __section(__versions) = { + { 0x9de7765d, "module_layout" }, + { 0xc5850110, "printk" }, + { 0xbdfb6dbb, "__fentry__" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "551436A789D4DFB9D4B008E"); diff --git a/hellow/hello.mod.o b/hellow/hello.mod.o new file mode 100644 index 0000000..22707b4 Binary files /dev/null and b/hellow/hello.mod.o differ diff --git a/hellow/modules.order b/hellow/modules.order new file mode 100644 index 0000000..8f3bb65 --- /dev/null +++ b/hellow/modules.order @@ -0,0 +1 @@ +/home/chao/code/module/hellow/hello.ko diff --git a/i2c/Makefile b/i2c/Makefile new file mode 100644 index 0000000..e436743 --- /dev/null +++ b/i2c/Makefile @@ -0,0 +1,30 @@ + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DDEBUG +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +#xxx-objs := main.o pipe.o access.o +obj-m := mpu6050.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + + +#depend .depend dep: +# $(CC) $(EXTRA_CFLAGS) -M *.c > .depend + +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif \ No newline at end of file diff --git a/i2c/mpu6050.c b/i2c/mpu6050.c new file mode 100644 index 0000000..bd50e36 --- /dev/null +++ b/i2c/mpu6050.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//宏定义 +#define SMPLRT_DIV 0x19 +#define CONFIG 0x1A +#define GYRO_CONFIG 0x1B +#define ACCEL_CONFIG 0x1C +#define ACCEL_XOUT_H 0x3B +#define ACCEL_XOUT_L 0x3C +#define ACCEL_YOUT_H 0x3D +#define ACCEL_YOUT_L 0x3E +#define ACCEL_ZOUT_H 0x3F +#define ACCEL_ZOUT_L 0x40 +#define TEMP_OUT_H 0x41 +#define TEMP_OUT_L 0x42 +#define GYRO_XOUT_H 0x43 +#define GYRO_XOUT_L 0x44 +#define GYRO_YOUT_H 0x45 +#define GYRO_YOUT_L 0x46 +#define GYRO_ZOUT_H 0x47 +#define GYRO_ZOUT_L 0x48 +#define PWR_MGMT_1 0x6B +#define WHO_AM_I 0x75 +#define SlaveAddress 0xD0 +#define Address 0x68 //MPU6050地址 +#define I2C_RETRIES 0x0701 +#define I2C_TIMEOUT 0x0702 +#define I2C_SLAVE 0x0703 //IIC从器件的地址设置 +#define I2C_BUS_MODE 0x0780 + + +static struct mpu6050_dev{ + struct cdev c_dev; + dev_t devno; + struct class *class; + struct device *device; + struct i2c_client *client; +}; + + +static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data) +{ + int error = 0; + u8 write_data[2]; + struct i2c_msg send_msg; //要发送的数据结构体 + + /*设置要发送的数据*/ + write_data[0] = address; + write_data[1] = data; + + /*发送 iic要写入的地址 reg*/ + send_msg.addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址 + send_msg.flags = 0; //标记为发送数据 + send_msg.buf = write_data; //写入的首地址 + send_msg.len = 2; //reg长度 + + /*执行发送*/ + error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1); + if (error != 1) + { + pr_err("\n i2c_transfer error \n"); + return -1; + } + return 0; +} +static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length) +{ + return 0; +} +static int mpu6050_init(struct i2c_client *mpu6050_client) +{ + int error = 0; + /*配置mpu6050*/ + error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00); + error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07); + error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06); + error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01); + + if (error < 0) + { + /*初始化错误*/ + pr_err("\n mpu6050_init error \n"); + return -1; + } + return 0; +} + +/*字符设备操作函数集,open函数实现*/ +static int mpu6050_open(struct inode *inode, struct file *filp) +{ + struct mpu6050_dev *mpu = NULL; + mpu = container_of(inode->i_cdev, struct mpu6050_dev, c_dev); + + filp->private_data = mpu; + + return mpu6050_init(mpu); + + +} +/*字符设备操作函数集,.read函数实现*/ +static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) +{ + return 0; +} +/*字符设备操作函数集,.release函数实现*/ +static int mpu6050_release(struct inode *inode, struct file *filp) +{ + return 0; +} +/*字符设备操作函数集*/ +static struct file_operations mpu6050_chr_dev_fops = + { + .owner = THIS_MODULE, + .open = mpu6050_open, + .read = mpu6050_read, + .release = mpu6050_release, +}; + +static struct mpu6050_dev mpu6050dev; + + +/*i2c总线设备函数集*/ +static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + ret = alloc_chrdev_region(&(mpu6050dev.devno),0,1,"mpu6050"); + if(ret < 0){ + pr_err("chrdev alloc region failed\n"); + return ret; + } + + pr_info("devno is %u(major:%u,minor:%u)\n",mpu6050dev.devno,MAJOR(mpu6050dev.devno), MINOR(mpu6050dev.devno)); + + cdev_init(&mpu6050dev.c_dev, &mpu6050_chr_dev_fops); + mpu6050dev.c_dev.owner = THIS_MODULE; + ret = cdev_add(&mpu6050dev.c_dev, mpu6050dev.devno, 1); + if(ret < 0){ + pr_err("chrdev add failed\n"); + goto cdev_add_err; + } + + ret = class_create(THIS_MODULE, "mpu_class"); + if(IS_ERR(ret)) + { + pr_err("create class faild!/n"); + goto class_create_err; + } + mpu6050dev.class = ret; + + ret = device_create(mpu6050dev.class,NULL,devno,NULL,"mpu6050"); + if(IS_ERR(ret)) + { + pr_err("device_create failed\n"); + goto device_create_err; + } + mpu6050dev.device = ret; + + mpu6050dev.client = client; + + return 0; + +device_create_err: + class_destroy(mpu6050dev.class); +class_create_err: + cdev_del(&mpu6050dev.c_dev); +cdev_add_err: + unregister_chrdev_region(mpu6050dev.devno, 1); + return ret; + +} +static int mpu6050_remove(struct i2c_client *client) +{ + + device_destroy(mpu6050dev.class, mpu6050dev.device); + class_destroy(mpu6050dev.class); + cdev_del(&mpu6050dev.c_dev); + unregister_chrdev_region(mpu6050dev.devno, 1); + + return 0; +} + +/*定义ID 匹配表*/ +static const struct i2c_device_id gtp_device_id[] = { + {"fire,i2c_mpu6050", 0}, + {}}; + +/*定义设备树匹配表*/ +static const struct of_device_id mpu6050_of_match_table[] = { + {.compatible = "fire,i2c_mpu6050"}, + {/* sentinel */}}; + + +/*定义i2c总线设备结构体*/ +struct i2c_driver mpu6050_driver = { + .probe = mpu6050_probe, + .remove = mpu6050_remove, + .id_table = gtp_device_id, + .driver = { + .name = "fire,i2c_mpu6050", + .owner = THIS_MODULE, + .of_match_table = mpu6050_of_match_table, + }, +}; + +/* + * 驱动初始化函数 + */ +static int __init mpu6050_driver_init(void) +{ + int ret; + pr_info("mpu6050_driver_init\n"); + ret = i2c_add_driver(&mpu6050_driver); + return 0; +} + +/* + * 驱动注销函数 + */ +static void __exit mpu6050_driver_exit(void) +{ + pr_info("mpu6050_driver_exit\n"); + i2c_del_driver(&mpu6050_driver); +} + +module_init(mpu6050_driver_init); +module_exit(mpu6050_driver_exit); + +MODULE_LICENSE("GPL"); + + + diff --git a/led_dev/.depend b/led_dev/.depend new file mode 100644 index 0000000..e69de29 diff --git a/led_dev/Makefile b/led_dev/Makefile new file mode 100644 index 0000000..1990535 --- /dev/null +++ b/led_dev/Makefile @@ -0,0 +1,29 @@ + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DLED_DEBUG +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +obj-m := led.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + + +#depend .depend dep: +# $(CC) $(EXTRA_CFLAGS) -M *.c > .depend + +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif \ No newline at end of file diff --git a/led_dev/led.c b/led_dev/led.c new file mode 100644 index 0000000..a7e7867 --- /dev/null +++ b/led_dev/led.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "led.h" + +static dev_t devno = 0; +static uint32_t led_major = LED_MAJOR; +static uint32_t led_minor = 0; + +static struct class *led_class; + + +static struct led_chrdev led_cdev[DEV_CNT] = { + {.pa_dr = 0x0209C000,.pa_gdir = 0x0209C004,.pa_iomuxc_mux = + 0x20E006C,.pa_ccm_ccgrx = 0x20C406C,.pa_iomux_pad = + 0x20E02F8,.led_pin = 4,.clock_offset = 26}, + {.pa_dr = 0x20A8000,.pa_gdir = 0x20A8004,.pa_iomuxc_mux = + 0x20E01E0,.pa_ccm_ccgrx = 0x20C4074,.pa_iomux_pad = + 0x20E046C,.led_pin = 20,.clock_offset = 12}, + {.pa_dr = 0x20A8000,.pa_gdir = 0x20A8004,.pa_iomuxc_mux = + 0x20E01DC,.pa_ccm_ccgrx = 0x20C4074,.pa_iomux_pad = + 0x20E0468,.led_pin = 19,.clock_offset = 12}, +}; + +static int led_open(struct inode *inode, struct file *filp) +{ + uint32_t val = 0; + struct led_chrdev *my_led_dev = NULL; + + PDEBUG("led open %d:%d...\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev)); + my_led_dev = container_of(inode->i_cdev, struct led_chrdev, dev); + + filp->private_data = my_led_dev; + + my_led_dev->va_ccm_ccgrx = ioremap(my_led_dev->pa_ccm_ccgrx,4); + my_led_dev->va_dr = ioremap(my_led_dev->pa_dr,4); + my_led_dev->va_gdir = ioremap(my_led_dev->pa_gdir,4); + my_led_dev->va_iomuxc_mux = ioremap(my_led_dev->pa_iomuxc_mux,4); + my_led_dev->va_iomux_pad = ioremap(my_led_dev->pa_iomux_pad,4); + + val = ioread32(my_led_dev->va_ccm_ccgrx); + val &= ~(3 << my_led_dev->clock_offset); + val |= 3 << my_led_dev->clock_offset; + + iowrite32(val, my_led_dev->va_ccm_ccgrx); + iowrite32(5, my_led_dev->va_iomuxc_mux); + iowrite32(0x1F838, my_led_dev->va_iomux_pad); + + val = ioread32(my_led_dev->va_gdir); + val &= ~(1 << my_led_dev->led_pin); + val |= (1 << my_led_dev->led_pin); + iowrite32(val, my_led_dev->va_gdir); + + val = ioread32(my_led_dev->va_dr); + val |= (0x01 << my_led_dev->led_pin); + iowrite32(val, my_led_dev->va_dr); + + return 0; +} + +static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) +{ + return cnt; +} + +static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt) +{ + uint32_t val = 0; + uint32_t ret = 0; + struct led_chrdev *my_led_dev = NULL; + + PDEBUG("led write...\n"); + my_led_dev = filp->private_data; + + kstrtoul_from_user(buf,cnt,10,&ret); + + PDEBUG("ret = %u\n",ret); + + val = ioread32(my_led_dev->va_dr); + + if (ret) + val &= ~(0x01 << my_led_dev->led_pin); + else + val |= (0x01 << my_led_dev->led_pin); + + iowrite32(val, my_led_dev->va_dr); + *offt += cnt; + + return cnt; + +} + +static int led_release(struct inode *inode, struct file *filp) +{ + struct led_chrdev *my_led_dev = NULL; + + my_led_dev = container_of(inode->i_cdev, struct led_chrdev, dev); + + + iounmap(my_led_dev->va_ccm_ccgrx); + iounmap(my_led_dev->va_dr); + iounmap(my_led_dev->va_gdir); + iounmap(my_led_dev->va_iomuxc_mux); + iounmap(my_led_dev->va_iomux_pad); + + return 0; +} + +static struct file_operations led_fops = { +.owner = THIS_MODULE, +.open = led_open, +.read = led_read, +.write = led_write, +.release = led_release, +}; + + +static int __init my_led_init(void) +{ + uint32_t ret = 0; + uint32_t i = 0; + dev_t cur_dev = 0; + printk(KERN_INFO "led Module Init start\n"); + + if(led_major){ + devno = MKDEV(led_major,led_minor); + ret = register_chrdev_region(devno, DEV_CNT, "led"); + } + else{ + + ret = alloc_chrdev_region(&devno, 0, DEV_CNT, "led"); + led_major = MAJOR(devno); + } + if(ret < 0){ + printk(KERN_ERR "chrdev alloc region failed\n"); + return ret; + } + + printk(KERN_INFO "devno is %u(0x%x)(major:%u,minor:%u)\n",devno,devno,led_major,led_minor); + + + led_class = class_create(THIS_MODULE, "led_class"); + if(IS_ERR(led_class)) + { + printk(KERN_ERR" create class faild!/n"); + return -EBUSY; + } + + for(i = 0; i< DEV_CNT; i++) + { + cdev_init(&led_cdev[i].dev, &led_fops); + led_cdev[i].dev.owner = THIS_MODULE; + cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i); + cdev_add(&led_cdev[i].dev, cur_dev, 1); + device_create(led_class,NULL,cur_dev,NULL,"led_%s",(i == 0)?"red":((i == 1)? "green":"blue")); + } + + return ret; +} + +static void __exit my_led_exit(void) +{ + uint32_t i = 0; + dev_t cur_dev = 0; + + for(i =0 ; i < DEV_CNT; i++){ + cur_dev = MKDEV(MAJOR(devno), MINOR(devno) + i); + device_destroy(led_class,cur_dev); + cdev_del(&led_cdev[i].dev); + } + class_destroy(led_class); + + + if(!devno) + unregister_chrdev_region(devno, DEV_CNT); + + printk("LED Module Exit\n"); +} + +module_init(my_led_init); +module_exit(my_led_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + diff --git a/led_dev/led.h b/led_dev/led.h new file mode 100644 index 0000000..c03ab3f --- /dev/null +++ b/led_dev/led.h @@ -0,0 +1,37 @@ +#ifndef _LED_H +#define _LED_H + +struct led_chrdev { + struct cdev dev; + unsigned int __iomem *va_dr; + unsigned int __iomem *va_gdir; + unsigned int __iomem *va_iomuxc_mux; + unsigned int __iomem *va_ccm_ccgrx; + unsigned int __iomem *va_iomux_pad; + + unsigned long pa_dr; + unsigned long pa_gdir; + unsigned long pa_iomuxc_mux; + unsigned long pa_ccm_ccgrx; + unsigned long pa_iomux_pad; + + unsigned int led_pin; + unsigned int clock_offset; +}; + + +#define DEV_CNT 3 + +#ifndef LED_MAJOR +#define LED_MAJOR 0 +#endif + + +#ifdef LED_DEBUG +#define PDEBUG(fmt, args...) printk(KERN_INFO "led_dev: " fmt, ##args) +#else +#define PDEBUG(fmt, args...) +#endif + + +#endif \ No newline at end of file diff --git a/my_first_module/.char_dev.ko.cmd b/my_first_module/.char_dev.ko.cmd new file mode 100644 index 0000000..ee080af --- /dev/null +++ b/my_first_module/.char_dev.ko.cmd @@ -0,0 +1 @@ +cmd_/home/chao/code/module/my_first_module/char_dev.ko := arm-linux-gnueabihf-ld -EL -r -T ./scripts/module-common.lds --build-id -o /home/chao/code/module/my_first_module/char_dev.ko /home/chao/code/module/my_first_module/char_dev.o /home/chao/code/module/my_first_module/char_dev.mod.o diff --git a/my_first_module/.char_dev.mod.o.cmd b/my_first_module/.char_dev.mod.o.cmd new file mode 100644 index 0000000..60523b5 --- /dev/null +++ b/my_first_module/.char_dev.mod.o.cmd @@ -0,0 +1,439 @@ +cmd_/home/chao/code/module/my_first_module/char_dev.mod.o := arm-linux-gnueabihf-gcc -Wp,-MD,/home/chao/code/module/my_first_module/.char_dev.mod.o.d -nostdinc -isystem /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4/include -I./arch/arm/include -Iarch/arm/include/generated/uapi -Iarch/arm/include/generated -Iinclude -I./arch/arm/include/uapi -Iarch/arm/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-dwarf2-cfi-asm -fno-ipa-sra -mabi=aapcs-linux -mno-thumb-interwork -mfpu=vfp -funwind-tables -marm -D__LINUX_ARM_ARCH__=7 -march=armv7-a -msoft-float -Uarm -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-but-set-variable -fomit-frame-pointer -fno-var-tracking-assignments -g -femit-struct-debug-baseonly -fno-var-tracking -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -DCC_HAVE_ASM_GOTO -D"KBUILD_STR(s)=\#s" -D"KBUILD_BASENAME=KBUILD_STR(char_dev.mod)" -D"KBUILD_MODNAME=KBUILD_STR(char_dev)" -DMODULE -c -o /home/chao/code/module/my_first_module/char_dev.mod.o /home/chao/code/module/my_first_module/char_dev.mod.c + +source_/home/chao/code/module/my_first_module/char_dev.mod.o := /home/chao/code/module/my_first_module/char_dev.mod.c + +deps_/home/chao/code/module/my_first_module/char_dev.mod.o := \ + $(wildcard include/config/module/unload.h) \ + include/linux/module.h \ + $(wildcard include/config/sysfs.h) \ + $(wildcard include/config/modules.h) \ + $(wildcard include/config/unused/symbols.h) \ + $(wildcard include/config/module/sig.h) \ + $(wildcard include/config/generic/bug.h) \ + $(wildcard include/config/kallsyms.h) \ + $(wildcard include/config/smp.h) \ + $(wildcard include/config/tracepoints.h) \ + $(wildcard include/config/tracing.h) \ + $(wildcard include/config/event/tracing.h) \ + $(wildcard include/config/ftrace/mcount/record.h) \ + $(wildcard include/config/livepatch.h) \ + $(wildcard include/config/constructors.h) \ + $(wildcard include/config/debug/set/module/ronx.h) \ + include/linux/list.h \ + $(wildcard include/config/debug/list.h) \ + include/linux/types.h \ + $(wildcard include/config/uid16.h) \ + $(wildcard include/config/lbdaf.h) \ + $(wildcard include/config/arch/dma/addr/t/64bit.h) \ + $(wildcard include/config/phys/addr/t/64bit.h) \ + $(wildcard include/config/64bit.h) \ + include/uapi/linux/types.h \ + arch/arm/include/asm/types.h \ + include/asm-generic/int-ll64.h \ + include/uapi/asm-generic/int-ll64.h \ + arch/arm/include/generated/asm/bitsperlong.h \ + include/asm-generic/bitsperlong.h \ + include/uapi/asm-generic/bitsperlong.h \ + include/uapi/linux/posix_types.h \ + include/linux/stddef.h \ + include/uapi/linux/stddef.h \ + include/linux/compiler.h \ + $(wildcard include/config/sparse/rcu/pointer.h) \ + $(wildcard include/config/trace/branch/profiling.h) \ + $(wildcard include/config/profile/all/branches.h) \ + $(wildcard include/config/enable/must/check.h) \ + $(wildcard include/config/enable/warn/deprecated.h) \ + $(wildcard include/config/kprobes.h) \ + include/linux/compiler-gcc.h \ + $(wildcard include/config/arch/supports/optimized/inlining.h) \ + $(wildcard include/config/optimize/inlining.h) \ + $(wildcard include/config/gcov/kernel.h) \ + $(wildcard include/config/arch/use/builtin/bswap.h) \ + arch/arm/include/uapi/asm/posix_types.h \ + include/uapi/asm-generic/posix_types.h \ + include/linux/poison.h \ + $(wildcard include/config/illegal/pointer/value.h) \ + include/uapi/linux/const.h \ + include/linux/kernel.h \ + $(wildcard include/config/preempt/voluntary.h) \ + $(wildcard include/config/debug/atomic/sleep.h) \ + $(wildcard include/config/mmu.h) \ + $(wildcard include/config/prove/locking.h) \ + $(wildcard include/config/panic/timeout.h) \ + $(wildcard include/config/ring/buffer.h) \ + /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4/include/stdarg.h \ + include/linux/linkage.h \ + include/linux/stringify.h \ + include/linux/export.h \ + $(wildcard include/config/have/underscore/symbol/prefix.h) \ + $(wildcard include/config/modversions.h) \ + arch/arm/include/asm/linkage.h \ + include/linux/bitops.h \ + arch/arm/include/asm/bitops.h \ + include/linux/irqflags.h \ + $(wildcard include/config/trace/irqflags.h) \ + $(wildcard include/config/irqsoff/tracer.h) \ + $(wildcard include/config/preempt/tracer.h) \ + $(wildcard include/config/trace/irqflags/support.h) \ + include/linux/typecheck.h \ + arch/arm/include/asm/irqflags.h \ + $(wildcard include/config/cpu/v7m.h) \ + arch/arm/include/asm/ptrace.h \ + $(wildcard include/config/arm/thumb.h) \ + $(wildcard include/config/thumb2/kernel.h) \ + arch/arm/include/uapi/asm/ptrace.h \ + $(wildcard include/config/cpu/endian/be8.h) \ + arch/arm/include/asm/hwcap.h \ + arch/arm/include/uapi/asm/hwcap.h \ + arch/arm/include/asm/barrier.h \ + $(wildcard include/config/cpu/32v6k.h) \ + $(wildcard include/config/cpu/xsc3.h) \ + $(wildcard include/config/cpu/fa526.h) \ + $(wildcard include/config/arch/has/barriers.h) \ + $(wildcard include/config/arm/dma/mem/bufferable.h) \ + arch/arm/include/asm/outercache.h \ + $(wildcard include/config/outer/cache/sync.h) \ + $(wildcard include/config/outer/cache.h) \ + include/asm-generic/bitops/non-atomic.h \ + include/asm-generic/bitops/fls64.h \ + include/asm-generic/bitops/sched.h \ + include/asm-generic/bitops/hweight.h \ + include/asm-generic/bitops/arch_hweight.h \ + include/asm-generic/bitops/const_hweight.h \ + include/asm-generic/bitops/lock.h \ + include/asm-generic/bitops/le.h \ + arch/arm/include/uapi/asm/byteorder.h \ + include/linux/byteorder/little_endian.h \ + include/uapi/linux/byteorder/little_endian.h \ + include/linux/swab.h \ + include/uapi/linux/swab.h \ + arch/arm/include/asm/swab.h \ + arch/arm/include/uapi/asm/swab.h \ + include/linux/byteorder/generic.h \ + include/asm-generic/bitops/ext2-atomic-setbit.h \ + include/linux/log2.h \ + $(wildcard include/config/arch/has/ilog2/u32.h) \ + $(wildcard include/config/arch/has/ilog2/u64.h) \ + include/linux/printk.h \ + $(wildcard include/config/message/loglevel/default.h) \ + $(wildcard include/config/early/printk.h) \ + $(wildcard include/config/printk.h) \ + $(wildcard include/config/dynamic/debug.h) \ + include/linux/init.h \ + $(wildcard include/config/broken/rodata.h) \ + $(wildcard include/config/lto.h) \ + include/linux/kern_levels.h \ + include/linux/cache.h \ + $(wildcard include/config/arch/has/cache/line/size.h) \ + include/uapi/linux/kernel.h \ + include/uapi/linux/sysinfo.h \ + arch/arm/include/asm/cache.h \ + $(wildcard include/config/arm/l1/cache/shift.h) \ + $(wildcard include/config/aeabi.h) \ + include/linux/dynamic_debug.h \ + include/linux/string.h \ + $(wildcard include/config/binary/printf.h) \ + include/uapi/linux/string.h \ + arch/arm/include/asm/string.h \ + include/linux/errno.h \ + include/uapi/linux/errno.h \ + arch/arm/include/generated/asm/errno.h \ + include/uapi/asm-generic/errno.h \ + include/uapi/asm-generic/errno-base.h \ + arch/arm/include/asm/div64.h \ + arch/arm/include/asm/compiler.h \ + arch/arm/include/asm/bug.h \ + $(wildcard include/config/bug.h) \ + $(wildcard include/config/debug/bugverbose.h) \ + $(wildcard include/config/arm/lpae.h) \ + arch/arm/include/asm/opcodes.h \ + $(wildcard include/config/cpu/endian/be32.h) \ + include/asm-generic/bug.h \ + $(wildcard include/config/generic/bug/relative/pointers.h) \ + include/linux/stat.h \ + arch/arm/include/uapi/asm/stat.h \ + include/uapi/linux/stat.h \ + include/linux/time.h \ + $(wildcard include/config/arch/uses/gettimeoffset.h) \ + include/linux/seqlock.h \ + $(wildcard include/config/debug/lock/alloc.h) \ + include/linux/spinlock.h \ + $(wildcard include/config/debug/spinlock.h) \ + $(wildcard include/config/generic/lockbreak.h) \ + $(wildcard include/config/preempt.h) \ + include/linux/preempt.h \ + $(wildcard include/config/debug/preempt.h) \ + $(wildcard include/config/preempt/count.h) \ + $(wildcard include/config/context/tracking.h) \ + $(wildcard include/config/preempt/notifiers.h) \ + arch/arm/include/generated/asm/preempt.h \ + include/asm-generic/preempt.h \ + include/linux/thread_info.h \ + $(wildcard include/config/compat.h) \ + $(wildcard include/config/debug/stack/usage.h) \ + include/linux/bug.h \ + arch/arm/include/asm/thread_info.h \ + $(wildcard include/config/crunch.h) \ + $(wildcard include/config/arm/thumbee.h) \ + arch/arm/include/asm/fpstate.h \ + $(wildcard include/config/vfpv3.h) \ + $(wildcard include/config/iwmmxt.h) \ + arch/arm/include/asm/page.h \ + $(wildcard include/config/cpu/copy/v4wt.h) \ + $(wildcard include/config/cpu/copy/v4wb.h) \ + $(wildcard include/config/cpu/copy/feroceon.h) \ + $(wildcard include/config/cpu/copy/fa.h) \ + $(wildcard include/config/cpu/sa1100.h) \ + $(wildcard include/config/cpu/xscale.h) \ + $(wildcard include/config/cpu/copy/v6.h) \ + $(wildcard include/config/kuser/helpers.h) \ + $(wildcard include/config/have/arch/pfn/valid.h) \ + arch/arm/include/asm/glue.h \ + arch/arm/include/asm/pgtable-2level-types.h \ + arch/arm/include/asm/memory.h \ + $(wildcard include/config/need/mach/memory/h.h) \ + $(wildcard include/config/page/offset.h) \ + $(wildcard include/config/highmem.h) \ + $(wildcard include/config/dram/base.h) \ + $(wildcard include/config/dram/size.h) \ + $(wildcard include/config/have/tcm.h) \ + $(wildcard include/config/arm/patch/phys/virt.h) \ + $(wildcard include/config/phys/offset.h) \ + $(wildcard include/config/virt/to/bus.h) \ + include/linux/sizes.h \ + include/asm-generic/memory_model.h \ + $(wildcard include/config/flatmem.h) \ + $(wildcard include/config/discontigmem.h) \ + $(wildcard include/config/sparsemem/vmemmap.h) \ + $(wildcard include/config/sparsemem.h) \ + include/asm-generic/getorder.h \ + arch/arm/include/asm/domain.h \ + $(wildcard include/config/io/36.h) \ + $(wildcard include/config/cpu/use/domains.h) \ + include/linux/bottom_half.h \ + include/linux/preempt_mask.h \ + include/linux/spinlock_types.h \ + arch/arm/include/asm/spinlock_types.h \ + include/linux/lockdep.h \ + $(wildcard include/config/lockdep.h) \ + $(wildcard include/config/lock/stat.h) \ + include/linux/rwlock_types.h \ + arch/arm/include/asm/spinlock.h \ + include/linux/prefetch.h \ + arch/arm/include/asm/processor.h \ + $(wildcard include/config/have/hw/breakpoint.h) \ + $(wildcard include/config/arm/errata/754327.h) \ + arch/arm/include/asm/hw_breakpoint.h \ + arch/arm/include/asm/unified.h \ + $(wildcard include/config/arm/asm/unified.h) \ + include/linux/rwlock.h \ + include/linux/spinlock_api_smp.h \ + $(wildcard include/config/inline/spin/lock.h) \ + $(wildcard include/config/inline/spin/lock/bh.h) \ + $(wildcard include/config/inline/spin/lock/irq.h) \ + $(wildcard include/config/inline/spin/lock/irqsave.h) \ + $(wildcard include/config/inline/spin/trylock.h) \ + $(wildcard include/config/inline/spin/trylock/bh.h) \ + $(wildcard include/config/uninline/spin/unlock.h) \ + $(wildcard include/config/inline/spin/unlock/bh.h) \ + $(wildcard include/config/inline/spin/unlock/irq.h) \ + $(wildcard include/config/inline/spin/unlock/irqrestore.h) \ + include/linux/rwlock_api_smp.h \ + $(wildcard include/config/inline/read/lock.h) \ + $(wildcard include/config/inline/write/lock.h) \ + $(wildcard include/config/inline/read/lock/bh.h) \ + $(wildcard include/config/inline/write/lock/bh.h) \ + $(wildcard include/config/inline/read/lock/irq.h) \ + $(wildcard include/config/inline/write/lock/irq.h) \ + $(wildcard include/config/inline/read/lock/irqsave.h) \ + $(wildcard include/config/inline/write/lock/irqsave.h) \ + $(wildcard include/config/inline/read/trylock.h) \ + $(wildcard include/config/inline/write/trylock.h) \ + $(wildcard include/config/inline/read/unlock.h) \ + $(wildcard include/config/inline/write/unlock.h) \ + $(wildcard include/config/inline/read/unlock/bh.h) \ + $(wildcard include/config/inline/write/unlock/bh.h) \ + $(wildcard include/config/inline/read/unlock/irq.h) \ + $(wildcard include/config/inline/write/unlock/irq.h) \ + $(wildcard include/config/inline/read/unlock/irqrestore.h) \ + $(wildcard include/config/inline/write/unlock/irqrestore.h) \ + include/linux/atomic.h \ + $(wildcard include/config/arch/has/atomic/or.h) \ + $(wildcard include/config/generic/atomic64.h) \ + arch/arm/include/asm/atomic.h \ + arch/arm/include/asm/cmpxchg.h \ + $(wildcard include/config/cpu/sa110.h) \ + $(wildcard include/config/cpu/v6.h) \ + include/asm-generic/cmpxchg-local.h \ + include/asm-generic/atomic-long.h \ + include/linux/math64.h \ + $(wildcard include/config/arch/supports/int128.h) \ + include/linux/time64.h \ + include/uapi/linux/time.h \ + include/linux/uidgid.h \ + $(wildcard include/config/multiuser.h) \ + $(wildcard include/config/user/ns.h) \ + include/linux/highuid.h \ + include/linux/kmod.h \ + include/linux/gfp.h \ + $(wildcard include/config/zone/dma.h) \ + $(wildcard include/config/zone/dma32.h) \ + $(wildcard include/config/numa.h) \ + $(wildcard include/config/pm/sleep.h) \ + $(wildcard include/config/cma.h) \ + include/linux/mmdebug.h \ + $(wildcard include/config/debug/vm.h) \ + $(wildcard include/config/debug/virtual.h) \ + include/linux/mmzone.h \ + $(wildcard include/config/force/max/zoneorder.h) \ + $(wildcard include/config/memory/isolation.h) \ + $(wildcard include/config/memcg.h) \ + $(wildcard include/config/memory/hotplug.h) \ + $(wildcard include/config/compaction.h) \ + $(wildcard include/config/have/memblock/node/map.h) \ + $(wildcard include/config/flat/node/mem/map.h) \ + $(wildcard include/config/page/extension.h) \ + $(wildcard include/config/no/bootmem.h) \ + $(wildcard include/config/numa/balancing.h) \ + $(wildcard include/config/have/memory/present.h) \ + $(wildcard include/config/have/memoryless/nodes.h) \ + $(wildcard include/config/need/node/memmap/size.h) \ + $(wildcard include/config/need/multiple/nodes.h) \ + $(wildcard include/config/have/arch/early/pfn/to/nid.h) \ + $(wildcard include/config/sparsemem/extreme.h) \ + $(wildcard include/config/nodes/span/other/nodes.h) \ + $(wildcard include/config/holes/in/zone.h) \ + $(wildcard include/config/arch/has/holes/memorymodel.h) \ + include/linux/wait.h \ + arch/arm/include/generated/asm/current.h \ + include/asm-generic/current.h \ + include/uapi/linux/wait.h \ + include/linux/threads.h \ + $(wildcard include/config/nr/cpus.h) \ + $(wildcard include/config/base/small.h) \ + include/linux/numa.h \ + $(wildcard include/config/nodes/shift.h) \ + include/linux/nodemask.h \ + $(wildcard include/config/movable/node.h) \ + include/linux/bitmap.h \ + include/linux/pageblock-flags.h \ + $(wildcard include/config/hugetlb/page.h) \ + $(wildcard include/config/hugetlb/page/size/variable.h) \ + include/linux/page-flags-layout.h \ + include/generated/bounds.h \ + include/linux/memory_hotplug.h \ + $(wildcard include/config/memory/hotremove.h) \ + $(wildcard include/config/have/arch/nodedata/extension.h) \ + $(wildcard include/config/have/bootmem/info/node.h) \ + include/linux/notifier.h \ + include/linux/mutex.h \ + $(wildcard include/config/debug/mutexes.h) \ + $(wildcard include/config/mutex/spin/on/owner.h) \ + include/linux/osq_lock.h \ + include/linux/rwsem.h \ + $(wildcard include/config/rwsem/spin/on/owner.h) \ + $(wildcard include/config/rwsem/generic/spinlock.h) \ + arch/arm/include/generated/asm/rwsem.h \ + include/asm-generic/rwsem.h \ + include/linux/srcu.h \ + include/linux/rcupdate.h \ + $(wildcard include/config/tiny/rcu.h) \ + $(wildcard include/config/tree/rcu.h) \ + $(wildcard include/config/preempt/rcu.h) \ + $(wildcard include/config/rcu/trace.h) \ + $(wildcard include/config/rcu/stall/common.h) \ + $(wildcard include/config/rcu/user/qs.h) \ + $(wildcard include/config/rcu/nocb/cpu.h) \ + $(wildcard include/config/tasks/rcu.h) \ + $(wildcard include/config/debug/objects/rcu/head.h) \ + $(wildcard include/config/hotplug/cpu.h) \ + $(wildcard include/config/prove/rcu.h) \ + $(wildcard include/config/rcu/boost.h) \ + $(wildcard include/config/rcu/nocb/cpu/all.h) \ + $(wildcard include/config/no/hz/full/sysidle.h) \ + include/linux/cpumask.h \ + $(wildcard include/config/cpumask/offstack.h) \ + $(wildcard include/config/debug/per/cpu/maps.h) \ + include/linux/completion.h \ + include/linux/debugobjects.h \ + $(wildcard include/config/debug/objects.h) \ + $(wildcard include/config/debug/objects/free.h) \ + include/linux/rcutree.h \ + include/linux/workqueue.h \ + $(wildcard include/config/debug/objects/work.h) \ + $(wildcard include/config/freezer.h) \ + include/linux/timer.h \ + $(wildcard include/config/timer/stats.h) \ + $(wildcard include/config/debug/objects/timers.h) \ + include/linux/ktime.h \ + include/linux/jiffies.h \ + include/linux/timex.h \ + include/uapi/linux/timex.h \ + include/uapi/linux/param.h \ + arch/arm/include/generated/asm/param.h \ + include/asm-generic/param.h \ + $(wildcard include/config/hz.h) \ + include/uapi/asm-generic/param.h \ + arch/arm/include/asm/timex.h \ + include/linux/timekeeping.h \ + include/linux/topology.h \ + $(wildcard include/config/use/percpu/numa/node/id.h) \ + $(wildcard include/config/sched/smt.h) \ + include/linux/smp.h \ + $(wildcard include/config/up/late/init.h) \ + include/linux/llist.h \ + $(wildcard include/config/arch/have/nmi/safe/cmpxchg.h) \ + arch/arm/include/asm/smp.h \ + include/linux/percpu.h \ + $(wildcard include/config/need/per/cpu/embed/first/chunk.h) \ + $(wildcard include/config/need/per/cpu/page/first/chunk.h) \ + $(wildcard include/config/have/setup/per/cpu/area.h) \ + include/linux/pfn.h \ + arch/arm/include/asm/percpu.h \ + include/asm-generic/percpu.h \ + include/linux/percpu-defs.h \ + $(wildcard include/config/debug/force/weak/per/cpu.h) \ + arch/arm/include/asm/topology.h \ + $(wildcard include/config/arm/cpu/topology.h) \ + include/asm-generic/topology.h \ + include/linux/sysctl.h \ + $(wildcard include/config/sysctl.h) \ + include/linux/rbtree.h \ + include/uapi/linux/sysctl.h \ + include/linux/elf.h \ + arch/arm/include/asm/elf.h \ + $(wildcard include/config/vdso.h) \ + arch/arm/include/asm/auxvec.h \ + arch/arm/include/uapi/asm/auxvec.h \ + arch/arm/include/asm/vdso_datapage.h \ + arch/arm/include/asm/user.h \ + include/uapi/linux/elf.h \ + include/uapi/linux/elf-em.h \ + include/linux/kobject.h \ + $(wildcard include/config/uevent/helper.h) \ + $(wildcard include/config/debug/kobject/release.h) \ + include/linux/sysfs.h \ + include/linux/kernfs.h \ + $(wildcard include/config/kernfs.h) \ + include/linux/err.h \ + include/linux/idr.h \ + include/linux/kobject_ns.h \ + include/linux/kref.h \ + include/linux/moduleparam.h \ + $(wildcard include/config/alpha.h) \ + $(wildcard include/config/ia64.h) \ + $(wildcard include/config/ppc64.h) \ + include/linux/jump_label.h \ + $(wildcard include/config/jump/label.h) \ + arch/arm/include/asm/module.h \ + $(wildcard include/config/arm/unwind.h) \ + include/asm-generic/module.h \ + $(wildcard include/config/have/mod/arch/specific.h) \ + $(wildcard include/config/modules/use/elf/rel.h) \ + $(wildcard include/config/modules/use/elf/rela.h) \ + include/linux/vermagic.h \ + include/generated/utsrelease.h \ + +/home/chao/code/module/my_first_module/char_dev.mod.o: $(deps_/home/chao/code/module/my_first_module/char_dev.mod.o) + +$(deps_/home/chao/code/module/my_first_module/char_dev.mod.o): diff --git a/my_first_module/.char_dev.o.cmd b/my_first_module/.char_dev.o.cmd new file mode 100644 index 0000000..635b93c --- /dev/null +++ b/my_first_module/.char_dev.o.cmd @@ -0,0 +1,652 @@ +cmd_/home/chao/code/module/my_first_module/char_dev.o := arm-linux-gnueabihf-gcc -Wp,-MD,/home/chao/code/module/my_first_module/.char_dev.o.d -nostdinc -isystem /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4/include -I./arch/arm/include -Iarch/arm/include/generated/uapi -Iarch/arm/include/generated -Iinclude -I./arch/arm/include/uapi -Iarch/arm/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-dwarf2-cfi-asm -fno-ipa-sra -mabi=aapcs-linux -mno-thumb-interwork -mfpu=vfp -funwind-tables -marm -D__LINUX_ARM_ARCH__=7 -march=armv7-a -msoft-float -Uarm -fno-delete-null-pointer-checks -O2 --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-but-set-variable -fomit-frame-pointer -fno-var-tracking-assignments -g -femit-struct-debug-baseonly -fno-var-tracking -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -DCC_HAVE_ASM_GOTO -DMODULE -D"KBUILD_STR(s)=\#s" -D"KBUILD_BASENAME=KBUILD_STR(char_dev)" -D"KBUILD_MODNAME=KBUILD_STR(char_dev)" -c -o /home/chao/code/module/my_first_module/.tmp_char_dev.o /home/chao/code/module/my_first_module/char_dev.c + +source_/home/chao/code/module/my_first_module/char_dev.o := /home/chao/code/module/my_first_module/char_dev.c + +deps_/home/chao/code/module/my_first_module/char_dev.o := \ + include/linux/module.h \ + $(wildcard include/config/sysfs.h) \ + $(wildcard include/config/modules.h) \ + $(wildcard include/config/unused/symbols.h) \ + $(wildcard include/config/module/sig.h) \ + $(wildcard include/config/generic/bug.h) \ + $(wildcard include/config/kallsyms.h) \ + $(wildcard include/config/smp.h) \ + $(wildcard include/config/tracepoints.h) \ + $(wildcard include/config/tracing.h) \ + $(wildcard include/config/event/tracing.h) \ + $(wildcard include/config/ftrace/mcount/record.h) \ + $(wildcard include/config/livepatch.h) \ + $(wildcard include/config/module/unload.h) \ + $(wildcard include/config/constructors.h) \ + $(wildcard include/config/debug/set/module/ronx.h) \ + include/linux/list.h \ + $(wildcard include/config/debug/list.h) \ + include/linux/types.h \ + $(wildcard include/config/uid16.h) \ + $(wildcard include/config/lbdaf.h) \ + $(wildcard include/config/arch/dma/addr/t/64bit.h) \ + $(wildcard include/config/phys/addr/t/64bit.h) \ + $(wildcard include/config/64bit.h) \ + include/uapi/linux/types.h \ + arch/arm/include/asm/types.h \ + include/asm-generic/int-ll64.h \ + include/uapi/asm-generic/int-ll64.h \ + arch/arm/include/generated/asm/bitsperlong.h \ + include/asm-generic/bitsperlong.h \ + include/uapi/asm-generic/bitsperlong.h \ + include/uapi/linux/posix_types.h \ + include/linux/stddef.h \ + include/uapi/linux/stddef.h \ + include/linux/compiler.h \ + $(wildcard include/config/sparse/rcu/pointer.h) \ + $(wildcard include/config/trace/branch/profiling.h) \ + $(wildcard include/config/profile/all/branches.h) \ + $(wildcard include/config/enable/must/check.h) \ + $(wildcard include/config/enable/warn/deprecated.h) \ + $(wildcard include/config/kprobes.h) \ + include/linux/compiler-gcc.h \ + $(wildcard include/config/arch/supports/optimized/inlining.h) \ + $(wildcard include/config/optimize/inlining.h) \ + $(wildcard include/config/gcov/kernel.h) \ + $(wildcard include/config/arch/use/builtin/bswap.h) \ + arch/arm/include/uapi/asm/posix_types.h \ + include/uapi/asm-generic/posix_types.h \ + include/linux/poison.h \ + $(wildcard include/config/illegal/pointer/value.h) \ + include/uapi/linux/const.h \ + include/linux/kernel.h \ + $(wildcard include/config/preempt/voluntary.h) \ + $(wildcard include/config/debug/atomic/sleep.h) \ + $(wildcard include/config/mmu.h) \ + $(wildcard include/config/prove/locking.h) \ + $(wildcard include/config/panic/timeout.h) \ + $(wildcard include/config/ring/buffer.h) \ + /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4/include/stdarg.h \ + include/linux/linkage.h \ + include/linux/stringify.h \ + include/linux/export.h \ + $(wildcard include/config/have/underscore/symbol/prefix.h) \ + $(wildcard include/config/modversions.h) \ + arch/arm/include/asm/linkage.h \ + include/linux/bitops.h \ + arch/arm/include/asm/bitops.h \ + include/linux/irqflags.h \ + $(wildcard include/config/trace/irqflags.h) \ + $(wildcard include/config/irqsoff/tracer.h) \ + $(wildcard include/config/preempt/tracer.h) \ + $(wildcard include/config/trace/irqflags/support.h) \ + include/linux/typecheck.h \ + arch/arm/include/asm/irqflags.h \ + $(wildcard include/config/cpu/v7m.h) \ + arch/arm/include/asm/ptrace.h \ + $(wildcard include/config/arm/thumb.h) \ + $(wildcard include/config/thumb2/kernel.h) \ + arch/arm/include/uapi/asm/ptrace.h \ + $(wildcard include/config/cpu/endian/be8.h) \ + arch/arm/include/asm/hwcap.h \ + arch/arm/include/uapi/asm/hwcap.h \ + arch/arm/include/asm/barrier.h \ + $(wildcard include/config/cpu/32v6k.h) \ + $(wildcard include/config/cpu/xsc3.h) \ + $(wildcard include/config/cpu/fa526.h) \ + $(wildcard include/config/arch/has/barriers.h) \ + $(wildcard include/config/arm/dma/mem/bufferable.h) \ + arch/arm/include/asm/outercache.h \ + $(wildcard include/config/outer/cache/sync.h) \ + $(wildcard include/config/outer/cache.h) \ + include/asm-generic/bitops/non-atomic.h \ + include/asm-generic/bitops/fls64.h \ + include/asm-generic/bitops/sched.h \ + include/asm-generic/bitops/hweight.h \ + include/asm-generic/bitops/arch_hweight.h \ + include/asm-generic/bitops/const_hweight.h \ + include/asm-generic/bitops/lock.h \ + include/asm-generic/bitops/le.h \ + arch/arm/include/uapi/asm/byteorder.h \ + include/linux/byteorder/little_endian.h \ + include/uapi/linux/byteorder/little_endian.h \ + include/linux/swab.h \ + include/uapi/linux/swab.h \ + arch/arm/include/asm/swab.h \ + arch/arm/include/uapi/asm/swab.h \ + include/linux/byteorder/generic.h \ + include/asm-generic/bitops/ext2-atomic-setbit.h \ + include/linux/log2.h \ + $(wildcard include/config/arch/has/ilog2/u32.h) \ + $(wildcard include/config/arch/has/ilog2/u64.h) \ + include/linux/printk.h \ + $(wildcard include/config/message/loglevel/default.h) \ + $(wildcard include/config/early/printk.h) \ + $(wildcard include/config/printk.h) \ + $(wildcard include/config/dynamic/debug.h) \ + include/linux/init.h \ + $(wildcard include/config/broken/rodata.h) \ + $(wildcard include/config/lto.h) \ + include/linux/kern_levels.h \ + include/linux/cache.h \ + $(wildcard include/config/arch/has/cache/line/size.h) \ + include/uapi/linux/kernel.h \ + include/uapi/linux/sysinfo.h \ + arch/arm/include/asm/cache.h \ + $(wildcard include/config/arm/l1/cache/shift.h) \ + $(wildcard include/config/aeabi.h) \ + include/linux/dynamic_debug.h \ + include/linux/string.h \ + $(wildcard include/config/binary/printf.h) \ + include/uapi/linux/string.h \ + arch/arm/include/asm/string.h \ + include/linux/errno.h \ + include/uapi/linux/errno.h \ + arch/arm/include/generated/asm/errno.h \ + include/uapi/asm-generic/errno.h \ + include/uapi/asm-generic/errno-base.h \ + arch/arm/include/asm/div64.h \ + arch/arm/include/asm/compiler.h \ + arch/arm/include/asm/bug.h \ + $(wildcard include/config/bug.h) \ + $(wildcard include/config/debug/bugverbose.h) \ + $(wildcard include/config/arm/lpae.h) \ + arch/arm/include/asm/opcodes.h \ + $(wildcard include/config/cpu/endian/be32.h) \ + include/asm-generic/bug.h \ + $(wildcard include/config/generic/bug/relative/pointers.h) \ + include/linux/stat.h \ + arch/arm/include/uapi/asm/stat.h \ + include/uapi/linux/stat.h \ + include/linux/time.h \ + $(wildcard include/config/arch/uses/gettimeoffset.h) \ + include/linux/seqlock.h \ + $(wildcard include/config/debug/lock/alloc.h) \ + include/linux/spinlock.h \ + $(wildcard include/config/debug/spinlock.h) \ + $(wildcard include/config/generic/lockbreak.h) \ + $(wildcard include/config/preempt.h) \ + include/linux/preempt.h \ + $(wildcard include/config/debug/preempt.h) \ + $(wildcard include/config/preempt/count.h) \ + $(wildcard include/config/context/tracking.h) \ + $(wildcard include/config/preempt/notifiers.h) \ + arch/arm/include/generated/asm/preempt.h \ + include/asm-generic/preempt.h \ + include/linux/thread_info.h \ + $(wildcard include/config/compat.h) \ + $(wildcard include/config/debug/stack/usage.h) \ + include/linux/bug.h \ + arch/arm/include/asm/thread_info.h \ + $(wildcard include/config/crunch.h) \ + $(wildcard include/config/arm/thumbee.h) \ + arch/arm/include/asm/fpstate.h \ + $(wildcard include/config/vfpv3.h) \ + $(wildcard include/config/iwmmxt.h) \ + arch/arm/include/asm/page.h \ + $(wildcard include/config/cpu/copy/v4wt.h) \ + $(wildcard include/config/cpu/copy/v4wb.h) \ + $(wildcard include/config/cpu/copy/feroceon.h) \ + $(wildcard include/config/cpu/copy/fa.h) \ + $(wildcard include/config/cpu/sa1100.h) \ + $(wildcard include/config/cpu/xscale.h) \ + $(wildcard include/config/cpu/copy/v6.h) \ + $(wildcard include/config/kuser/helpers.h) \ + $(wildcard include/config/have/arch/pfn/valid.h) \ + arch/arm/include/asm/glue.h \ + arch/arm/include/asm/pgtable-2level-types.h \ + arch/arm/include/asm/memory.h \ + $(wildcard include/config/need/mach/memory/h.h) \ + $(wildcard include/config/page/offset.h) \ + $(wildcard include/config/highmem.h) \ + $(wildcard include/config/dram/base.h) \ + $(wildcard include/config/dram/size.h) \ + $(wildcard include/config/have/tcm.h) \ + $(wildcard include/config/arm/patch/phys/virt.h) \ + $(wildcard include/config/phys/offset.h) \ + $(wildcard include/config/virt/to/bus.h) \ + include/linux/sizes.h \ + include/asm-generic/memory_model.h \ + $(wildcard include/config/flatmem.h) \ + $(wildcard include/config/discontigmem.h) \ + $(wildcard include/config/sparsemem/vmemmap.h) \ + $(wildcard include/config/sparsemem.h) \ + include/asm-generic/getorder.h \ + arch/arm/include/asm/domain.h \ + $(wildcard include/config/io/36.h) \ + $(wildcard include/config/cpu/use/domains.h) \ + include/linux/bottom_half.h \ + include/linux/preempt_mask.h \ + include/linux/spinlock_types.h \ + arch/arm/include/asm/spinlock_types.h \ + include/linux/lockdep.h \ + $(wildcard include/config/lockdep.h) \ + $(wildcard include/config/lock/stat.h) \ + include/linux/rwlock_types.h \ + arch/arm/include/asm/spinlock.h \ + include/linux/prefetch.h \ + arch/arm/include/asm/processor.h \ + $(wildcard include/config/have/hw/breakpoint.h) \ + $(wildcard include/config/arm/errata/754327.h) \ + arch/arm/include/asm/hw_breakpoint.h \ + arch/arm/include/asm/unified.h \ + $(wildcard include/config/arm/asm/unified.h) \ + include/linux/rwlock.h \ + include/linux/spinlock_api_smp.h \ + $(wildcard include/config/inline/spin/lock.h) \ + $(wildcard include/config/inline/spin/lock/bh.h) \ + $(wildcard include/config/inline/spin/lock/irq.h) \ + $(wildcard include/config/inline/spin/lock/irqsave.h) \ + $(wildcard include/config/inline/spin/trylock.h) \ + $(wildcard include/config/inline/spin/trylock/bh.h) \ + $(wildcard include/config/uninline/spin/unlock.h) \ + $(wildcard include/config/inline/spin/unlock/bh.h) \ + $(wildcard include/config/inline/spin/unlock/irq.h) \ + $(wildcard include/config/inline/spin/unlock/irqrestore.h) \ + include/linux/rwlock_api_smp.h \ + $(wildcard include/config/inline/read/lock.h) \ + $(wildcard include/config/inline/write/lock.h) \ + $(wildcard include/config/inline/read/lock/bh.h) \ + $(wildcard include/config/inline/write/lock/bh.h) \ + $(wildcard include/config/inline/read/lock/irq.h) \ + $(wildcard include/config/inline/write/lock/irq.h) \ + $(wildcard include/config/inline/read/lock/irqsave.h) \ + $(wildcard include/config/inline/write/lock/irqsave.h) \ + $(wildcard include/config/inline/read/trylock.h) \ + $(wildcard include/config/inline/write/trylock.h) \ + $(wildcard include/config/inline/read/unlock.h) \ + $(wildcard include/config/inline/write/unlock.h) \ + $(wildcard include/config/inline/read/unlock/bh.h) \ + $(wildcard include/config/inline/write/unlock/bh.h) \ + $(wildcard include/config/inline/read/unlock/irq.h) \ + $(wildcard include/config/inline/write/unlock/irq.h) \ + $(wildcard include/config/inline/read/unlock/irqrestore.h) \ + $(wildcard include/config/inline/write/unlock/irqrestore.h) \ + include/linux/atomic.h \ + $(wildcard include/config/arch/has/atomic/or.h) \ + $(wildcard include/config/generic/atomic64.h) \ + arch/arm/include/asm/atomic.h \ + arch/arm/include/asm/cmpxchg.h \ + $(wildcard include/config/cpu/sa110.h) \ + $(wildcard include/config/cpu/v6.h) \ + include/asm-generic/cmpxchg-local.h \ + include/asm-generic/atomic-long.h \ + include/linux/math64.h \ + $(wildcard include/config/arch/supports/int128.h) \ + include/linux/time64.h \ + include/uapi/linux/time.h \ + include/linux/uidgid.h \ + $(wildcard include/config/multiuser.h) \ + $(wildcard include/config/user/ns.h) \ + include/linux/highuid.h \ + include/linux/kmod.h \ + include/linux/gfp.h \ + $(wildcard include/config/zone/dma.h) \ + $(wildcard include/config/zone/dma32.h) \ + $(wildcard include/config/numa.h) \ + $(wildcard include/config/pm/sleep.h) \ + $(wildcard include/config/cma.h) \ + include/linux/mmdebug.h \ + $(wildcard include/config/debug/vm.h) \ + $(wildcard include/config/debug/virtual.h) \ + include/linux/mmzone.h \ + $(wildcard include/config/force/max/zoneorder.h) \ + $(wildcard include/config/memory/isolation.h) \ + $(wildcard include/config/memcg.h) \ + $(wildcard include/config/memory/hotplug.h) \ + $(wildcard include/config/compaction.h) \ + $(wildcard include/config/have/memblock/node/map.h) \ + $(wildcard include/config/flat/node/mem/map.h) \ + $(wildcard include/config/page/extension.h) \ + $(wildcard include/config/no/bootmem.h) \ + $(wildcard include/config/numa/balancing.h) \ + $(wildcard include/config/have/memory/present.h) \ + $(wildcard include/config/have/memoryless/nodes.h) \ + $(wildcard include/config/need/node/memmap/size.h) \ + $(wildcard include/config/need/multiple/nodes.h) \ + $(wildcard include/config/have/arch/early/pfn/to/nid.h) \ + $(wildcard include/config/sparsemem/extreme.h) \ + $(wildcard include/config/nodes/span/other/nodes.h) \ + $(wildcard include/config/holes/in/zone.h) \ + $(wildcard include/config/arch/has/holes/memorymodel.h) \ + include/linux/wait.h \ + arch/arm/include/generated/asm/current.h \ + include/asm-generic/current.h \ + include/uapi/linux/wait.h \ + include/linux/threads.h \ + $(wildcard include/config/nr/cpus.h) \ + $(wildcard include/config/base/small.h) \ + include/linux/numa.h \ + $(wildcard include/config/nodes/shift.h) \ + include/linux/nodemask.h \ + $(wildcard include/config/movable/node.h) \ + include/linux/bitmap.h \ + include/linux/pageblock-flags.h \ + $(wildcard include/config/hugetlb/page.h) \ + $(wildcard include/config/hugetlb/page/size/variable.h) \ + include/linux/page-flags-layout.h \ + include/generated/bounds.h \ + include/linux/memory_hotplug.h \ + $(wildcard include/config/memory/hotremove.h) \ + $(wildcard include/config/have/arch/nodedata/extension.h) \ + $(wildcard include/config/have/bootmem/info/node.h) \ + include/linux/notifier.h \ + include/linux/mutex.h \ + $(wildcard include/config/debug/mutexes.h) \ + $(wildcard include/config/mutex/spin/on/owner.h) \ + include/linux/osq_lock.h \ + include/linux/rwsem.h \ + $(wildcard include/config/rwsem/spin/on/owner.h) \ + $(wildcard include/config/rwsem/generic/spinlock.h) \ + arch/arm/include/generated/asm/rwsem.h \ + include/asm-generic/rwsem.h \ + include/linux/srcu.h \ + include/linux/rcupdate.h \ + $(wildcard include/config/tiny/rcu.h) \ + $(wildcard include/config/tree/rcu.h) \ + $(wildcard include/config/preempt/rcu.h) \ + $(wildcard include/config/rcu/trace.h) \ + $(wildcard include/config/rcu/stall/common.h) \ + $(wildcard include/config/rcu/user/qs.h) \ + $(wildcard include/config/rcu/nocb/cpu.h) \ + $(wildcard include/config/tasks/rcu.h) \ + $(wildcard include/config/debug/objects/rcu/head.h) \ + $(wildcard include/config/hotplug/cpu.h) \ + $(wildcard include/config/prove/rcu.h) \ + $(wildcard include/config/rcu/boost.h) \ + $(wildcard include/config/rcu/nocb/cpu/all.h) \ + $(wildcard include/config/no/hz/full/sysidle.h) \ + include/linux/cpumask.h \ + $(wildcard include/config/cpumask/offstack.h) \ + $(wildcard include/config/debug/per/cpu/maps.h) \ + include/linux/completion.h \ + include/linux/debugobjects.h \ + $(wildcard include/config/debug/objects.h) \ + $(wildcard include/config/debug/objects/free.h) \ + include/linux/rcutree.h \ + include/linux/workqueue.h \ + $(wildcard include/config/debug/objects/work.h) \ + $(wildcard include/config/freezer.h) \ + include/linux/timer.h \ + $(wildcard include/config/timer/stats.h) \ + $(wildcard include/config/debug/objects/timers.h) \ + include/linux/ktime.h \ + include/linux/jiffies.h \ + include/linux/timex.h \ + include/uapi/linux/timex.h \ + include/uapi/linux/param.h \ + arch/arm/include/generated/asm/param.h \ + include/asm-generic/param.h \ + $(wildcard include/config/hz.h) \ + include/uapi/asm-generic/param.h \ + arch/arm/include/asm/timex.h \ + include/linux/timekeeping.h \ + include/linux/topology.h \ + $(wildcard include/config/use/percpu/numa/node/id.h) \ + $(wildcard include/config/sched/smt.h) \ + include/linux/smp.h \ + $(wildcard include/config/up/late/init.h) \ + include/linux/llist.h \ + $(wildcard include/config/arch/have/nmi/safe/cmpxchg.h) \ + arch/arm/include/asm/smp.h \ + include/linux/percpu.h \ + $(wildcard include/config/need/per/cpu/embed/first/chunk.h) \ + $(wildcard include/config/need/per/cpu/page/first/chunk.h) \ + $(wildcard include/config/have/setup/per/cpu/area.h) \ + include/linux/pfn.h \ + arch/arm/include/asm/percpu.h \ + include/asm-generic/percpu.h \ + include/linux/percpu-defs.h \ + $(wildcard include/config/debug/force/weak/per/cpu.h) \ + arch/arm/include/asm/topology.h \ + $(wildcard include/config/arm/cpu/topology.h) \ + include/asm-generic/topology.h \ + include/linux/sysctl.h \ + $(wildcard include/config/sysctl.h) \ + include/linux/rbtree.h \ + include/uapi/linux/sysctl.h \ + include/linux/elf.h \ + arch/arm/include/asm/elf.h \ + $(wildcard include/config/vdso.h) \ + arch/arm/include/asm/auxvec.h \ + arch/arm/include/uapi/asm/auxvec.h \ + arch/arm/include/asm/vdso_datapage.h \ + arch/arm/include/asm/user.h \ + include/uapi/linux/elf.h \ + include/uapi/linux/elf-em.h \ + include/linux/kobject.h \ + $(wildcard include/config/uevent/helper.h) \ + $(wildcard include/config/debug/kobject/release.h) \ + include/linux/sysfs.h \ + include/linux/kernfs.h \ + $(wildcard include/config/kernfs.h) \ + include/linux/err.h \ + include/linux/idr.h \ + include/linux/kobject_ns.h \ + include/linux/kref.h \ + include/linux/moduleparam.h \ + $(wildcard include/config/alpha.h) \ + $(wildcard include/config/ia64.h) \ + $(wildcard include/config/ppc64.h) \ + include/linux/jump_label.h \ + $(wildcard include/config/jump/label.h) \ + arch/arm/include/asm/module.h \ + $(wildcard include/config/arm/unwind.h) \ + include/asm-generic/module.h \ + $(wildcard include/config/have/mod/arch/specific.h) \ + $(wildcard include/config/modules/use/elf/rel.h) \ + $(wildcard include/config/modules/use/elf/rela.h) \ + include/linux/sched.h \ + $(wildcard include/config/sched/debug.h) \ + $(wildcard include/config/no/hz/common.h) \ + $(wildcard include/config/lockup/detector.h) \ + $(wildcard include/config/detect/hung/task.h) \ + $(wildcard include/config/core/dump/default/elf/headers.h) \ + $(wildcard include/config/sched/autogroup.h) \ + $(wildcard include/config/virt/cpu/accounting/native.h) \ + $(wildcard include/config/bsd/process/acct.h) \ + $(wildcard include/config/taskstats.h) \ + $(wildcard include/config/audit.h) \ + $(wildcard include/config/cgroups.h) \ + $(wildcard include/config/inotify/user.h) \ + $(wildcard include/config/fanotify.h) \ + $(wildcard include/config/epoll.h) \ + $(wildcard include/config/posix/mqueue.h) \ + $(wildcard include/config/keys.h) \ + $(wildcard include/config/perf/events.h) \ + $(wildcard include/config/schedstats.h) \ + $(wildcard include/config/task/delay/acct.h) \ + $(wildcard include/config/sched/mc.h) \ + $(wildcard include/config/fair/group/sched.h) \ + $(wildcard include/config/rt/group/sched.h) \ + $(wildcard include/config/cgroup/sched.h) \ + $(wildcard include/config/blk/dev/io/trace.h) \ + $(wildcard include/config/compat/brk.h) \ + $(wildcard include/config/memcg/kmem.h) \ + $(wildcard include/config/cc/stackprotector.h) \ + $(wildcard include/config/virt/cpu/accounting/gen.h) \ + $(wildcard include/config/sysvipc.h) \ + $(wildcard include/config/auditsyscall.h) \ + $(wildcard include/config/rt/mutexes.h) \ + $(wildcard include/config/block.h) \ + $(wildcard include/config/task/xacct.h) \ + $(wildcard include/config/cpusets.h) \ + $(wildcard include/config/futex.h) \ + $(wildcard include/config/fault/injection.h) \ + $(wildcard include/config/latencytop.h) \ + $(wildcard include/config/kasan.h) \ + $(wildcard include/config/function/graph/tracer.h) \ + $(wildcard include/config/uprobes.h) \ + $(wildcard include/config/bcache.h) \ + $(wildcard include/config/have/unstable/sched/clock.h) \ + $(wildcard include/config/irq/time/accounting.h) \ + $(wildcard include/config/no/hz/full.h) \ + $(wildcard include/config/proc/fs.h) \ + $(wildcard include/config/stack/growsup.h) \ + include/uapi/linux/sched.h \ + include/linux/sched/prio.h \ + include/linux/capability.h \ + include/uapi/linux/capability.h \ + include/linux/plist.h \ + $(wildcard include/config/debug/pi/list.h) \ + include/linux/mm_types.h \ + $(wildcard include/config/split/ptlock/cpus.h) \ + $(wildcard include/config/arch/enable/split/pmd/ptlock.h) \ + $(wildcard include/config/have/cmpxchg/double.h) \ + $(wildcard include/config/have/aligned/struct/page.h) \ + $(wildcard include/config/transparent/hugepage.h) \ + $(wildcard include/config/kmemcheck.h) \ + $(wildcard include/config/pgtable/levels.h) \ + $(wildcard include/config/aio.h) \ + $(wildcard include/config/mmu/notifier.h) \ + $(wildcard include/config/x86/intel/mpx.h) \ + include/linux/auxvec.h \ + include/uapi/linux/auxvec.h \ + include/linux/uprobes.h \ + arch/arm/include/asm/mmu.h \ + $(wildcard include/config/cpu/has/asid.h) \ + include/linux/cputime.h \ + arch/arm/include/generated/asm/cputime.h \ + include/asm-generic/cputime.h \ + $(wildcard include/config/virt/cpu/accounting.h) \ + include/asm-generic/cputime_jiffies.h \ + include/linux/sem.h \ + include/uapi/linux/sem.h \ + include/linux/ipc.h \ + include/uapi/linux/ipc.h \ + arch/arm/include/generated/asm/ipcbuf.h \ + include/uapi/asm-generic/ipcbuf.h \ + arch/arm/include/generated/asm/sembuf.h \ + include/uapi/asm-generic/sembuf.h \ + include/linux/shm.h \ + include/uapi/linux/shm.h \ + arch/arm/include/generated/asm/shmbuf.h \ + include/uapi/asm-generic/shmbuf.h \ + arch/arm/include/asm/shmparam.h \ + include/linux/signal.h \ + $(wildcard include/config/old/sigaction.h) \ + include/uapi/linux/signal.h \ + arch/arm/include/asm/signal.h \ + arch/arm/include/uapi/asm/signal.h \ + include/uapi/asm-generic/signal-defs.h \ + arch/arm/include/uapi/asm/sigcontext.h \ + arch/arm/include/generated/asm/siginfo.h \ + include/asm-generic/siginfo.h \ + include/uapi/asm-generic/siginfo.h \ + include/linux/pid.h \ + include/linux/proportions.h \ + include/linux/percpu_counter.h \ + include/linux/seccomp.h \ + $(wildcard include/config/seccomp.h) \ + $(wildcard include/config/have/arch/seccomp/filter.h) \ + $(wildcard include/config/seccomp/filter.h) \ + include/uapi/linux/seccomp.h \ + include/linux/rculist.h \ + include/linux/rtmutex.h \ + $(wildcard include/config/debug/rt/mutexes.h) \ + include/linux/resource.h \ + include/uapi/linux/resource.h \ + arch/arm/include/generated/asm/resource.h \ + include/asm-generic/resource.h \ + include/uapi/asm-generic/resource.h \ + include/linux/hrtimer.h \ + $(wildcard include/config/high/res/timers.h) \ + $(wildcard include/config/timerfd.h) \ + include/linux/timerqueue.h \ + include/linux/task_io_accounting.h \ + $(wildcard include/config/task/io/accounting.h) \ + include/linux/latencytop.h \ + include/linux/cred.h \ + $(wildcard include/config/debug/credentials.h) \ + $(wildcard include/config/security.h) \ + include/linux/key.h \ + include/linux/assoc_array.h \ + $(wildcard include/config/associative/array.h) \ + include/linux/selinux.h \ + $(wildcard include/config/security/selinux.h) \ + include/uapi/linux/magic.h \ + include/linux/cdev.h \ + include/linux/kdev_t.h \ + include/uapi/linux/kdev_t.h \ + include/linux/fs.h \ + $(wildcard include/config/fs/posix/acl.h) \ + $(wildcard include/config/ima.h) \ + $(wildcard include/config/fsnotify.h) \ + $(wildcard include/config/file/locking.h) \ + $(wildcard include/config/quota.h) \ + $(wildcard include/config/fs/dax.h) \ + $(wildcard include/config/migration.h) \ + include/linux/dcache.h \ + include/linux/rculist_bl.h \ + include/linux/list_bl.h \ + include/linux/bit_spinlock.h \ + include/linux/lockref.h \ + $(wildcard include/config/arch/use/cmpxchg/lockref.h) \ + include/linux/path.h \ + include/linux/list_lru.h \ + include/linux/shrinker.h \ + include/linux/radix-tree.h \ + include/linux/semaphore.h \ + include/uapi/linux/fiemap.h \ + include/linux/migrate_mode.h \ + include/linux/percpu-rwsem.h \ + include/linux/blk_types.h \ + $(wildcard include/config/blk/cgroup.h) \ + $(wildcard include/config/blk/dev/integrity.h) \ + include/uapi/linux/fs.h \ + include/uapi/linux/limits.h \ + include/uapi/linux/ioctl.h \ + arch/arm/include/generated/asm/ioctl.h \ + include/asm-generic/ioctl.h \ + include/uapi/asm-generic/ioctl.h \ + include/linux/quota.h \ + $(wildcard include/config/quota/netlink/interface.h) \ + include/uapi/linux/dqblk_xfs.h \ + include/linux/dqblk_v1.h \ + include/linux/dqblk_v2.h \ + include/linux/dqblk_qtree.h \ + include/linux/projid.h \ + include/uapi/linux/quota.h \ + include/linux/nfs_fs_i.h \ + include/linux/fcntl.h \ + include/uapi/linux/fcntl.h \ + arch/arm/include/uapi/asm/fcntl.h \ + include/uapi/asm-generic/fcntl.h \ + include/linux/slab.h \ + $(wildcard include/config/debug/slab.h) \ + $(wildcard include/config/failslab.h) \ + $(wildcard include/config/slab.h) \ + $(wildcard include/config/slub.h) \ + $(wildcard include/config/slob.h) \ + include/linux/kmemleak.h \ + $(wildcard include/config/debug/kmemleak.h) \ + include/linux/kasan.h \ + $(wildcard include/config/kasan/shadow/offset.h) \ + include/linux/uaccess.h \ + arch/arm/include/asm/uaccess.h \ + $(wildcard include/config/have/efficient/unaligned/access.h) \ + include/linux/device.h \ + $(wildcard include/config/debug/devres.h) \ + $(wildcard include/config/pinctrl.h) \ + $(wildcard include/config/dma/cma.h) \ + $(wildcard include/config/of.h) \ + $(wildcard include/config/devtmpfs.h) \ + $(wildcard include/config/sysfs/deprecated.h) \ + include/linux/ioport.h \ + include/linux/klist.h \ + include/linux/pinctrl/devinfo.h \ + $(wildcard include/config/pm.h) \ + include/linux/pinctrl/consumer.h \ + include/linux/seq_file.h \ + include/linux/pinctrl/pinctrl-state.h \ + include/linux/pm.h \ + $(wildcard include/config/vt/console/sleep.h) \ + $(wildcard include/config/pm/clk.h) \ + $(wildcard include/config/pm/generic/domains.h) \ + include/linux/ratelimit.h \ + arch/arm/include/asm/device.h \ + $(wildcard include/config/dmabounce.h) \ + $(wildcard include/config/iommu/api.h) \ + $(wildcard include/config/arm/dma/use/iommu.h) \ + $(wildcard include/config/arch/omap.h) \ + include/linux/pm_wakeup.h \ + /home/chao/code/module/my_first_module/char_dev.h \ + +/home/chao/code/module/my_first_module/char_dev.o: $(deps_/home/chao/code/module/my_first_module/char_dev.o) + +$(deps_/home/chao/code/module/my_first_module/char_dev.o): diff --git a/my_first_module/.tmp_versions/char_dev.mod b/my_first_module/.tmp_versions/char_dev.mod new file mode 100644 index 0000000..3cf34d5 --- /dev/null +++ b/my_first_module/.tmp_versions/char_dev.mod @@ -0,0 +1,2 @@ +/home/chao/code/module/my_first_module/char_dev.ko +/home/chao/code/module/my_first_module/char_dev.o diff --git a/my_first_module/Makefile b/my_first_module/Makefile new file mode 100644 index 0000000..dcbc31c --- /dev/null +++ b/my_first_module/Makefile @@ -0,0 +1,17 @@ +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +obj-m := char_dev.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean copy + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + +copy: + sudo cp *.ko /home/embedfire/workdir \ No newline at end of file diff --git a/my_first_module/Module.symvers b/my_first_module/Module.symvers new file mode 100644 index 0000000..e69de29 diff --git a/my_first_module/char_dev.c b/my_first_module/char_dev.c new file mode 100644 index 0000000..8f481f8 --- /dev/null +++ b/my_first_module/char_dev.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "char_dev.h" + + +static dev_t devno = 0; +static uint32_t my_fifo_major = MY_FIFO_MAJOR; +static uint32_t my_fifo_minor = 0; + +static struct class *myclass; + + +static int my_fifo_size = MY_FIFO_SIZE; +module_param(my_fifo_size, int, S_IRUGO); + + +struct my_fifo_dev{ + wait_queue_head_t readq, writeq; + char *fifo_begain,*fifo_end; + uint32_t fifo_size; + char *rp,*wp; + uint32_t nreaders,nwriters; + struct semaphore sem; + struct cdev my_cdev; +}; + +static struct my_fifo_dev *my_fifo_device_p = NULL; + +/* 打开设备 */ +static int my_fifo_open(struct inode *inode, struct file *filp) +{ + struct my_fifo_dev *my_fifo_dev = NULL; + + my_fifo_dev = container_of(inode->i_cdev, struct my_fifo_dev, my_cdev); + filp->private_data = my_fifo_dev; + + //printk(KERN_INFO "[ KERN_INFO ] my_fifo_dev addr = 0x%x\n",my_fifo_dev); + + + if (down_interruptible(&my_fifo_dev->sem)) + return -ERESTARTSYS; + + if(!my_fifo_dev->fifo_begain){ + //printk(KERN_INFO "[ KERN_INFO ] fifo is empty, kmalloc it ...\n"); + my_fifo_dev->fifo_begain = kmalloc(my_fifo_size, GFP_KERNEL); + if(!my_fifo_dev->fifo_begain){ + up(&my_fifo_dev->sem); + return -ENOMEM; + } + + my_fifo_dev->fifo_size = my_fifo_size; + my_fifo_dev->fifo_end = my_fifo_dev->fifo_begain + my_fifo_size; + my_fifo_dev->rp = my_fifo_dev->wp = my_fifo_dev->fifo_begain; + } + + //printk(KERN_INFO "[ KERN_INFO ] open...rp:0x%p,wp:0x%p\n",my_fifo_dev->rp,my_fifo_dev->wp); + + + /* use f_mode,not f_flags: it's cleaner (fs/open.c tells why) */ + if (filp->f_mode & FMODE_READ) + my_fifo_dev->nreaders++; + if (filp->f_mode & FMODE_WRITE) + my_fifo_dev->nwriters++; + up(&my_fifo_dev->sem); + + + return nonseekable_open(inode,filp); +} + + +static ssize_t my_fifo_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) +{ + struct my_fifo_dev *my_fifo_dev = filp->private_data; + uint32_t read_cont = 0; + + if (!(filp->f_mode & FMODE_READ)){ + printk(KERN_ERR "[ KERN_ERR ] read is not permitted\n"); + return -EPERM; + } + + if(down_interruptible(&my_fifo_dev->sem)){ + printk(KERN_ERR "[ KERN_ERR ] down_interruptible err\n"); + return -ERESTARTSYS; + } + + while(my_fifo_dev->rp == my_fifo_dev->wp){ + up(&my_fifo_dev->sem); + + if(filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + //printk(KERN_INFO "[ KERN_INFO ] rp:0x%p,wp:0x%p\n",my_fifo_dev->rp,my_fifo_dev->wp); + //printk(KERN_INFO "[ KERN_INFO ] waiting...\n"); + + if(wait_event_interruptible(my_fifo_dev->readq, (my_fifo_dev->rp != my_fifo_dev->wp))) + return -ERESTARTSYS; + + //printk(KERN_INFO "[ KERN_INFO ] wake up!\n"); + + if(down_interruptible(&my_fifo_dev->sem)) + return -ERESTARTSYS; + } + + if(my_fifo_dev->rp < my_fifo_dev->wp){ + read_cont = min(my_fifo_dev->wp - my_fifo_dev->rp, cnt); + } + else{ + read_cont = min(my_fifo_dev->fifo_end - my_fifo_dev->rp , cnt); + } + + //printk(KERN_INFO "[ KERN_INFO ] read %d bytes\n",read_cont); + + if(copy_to_user(buf, my_fifo_dev->rp, read_cont)){ + up(&my_fifo_dev->sem); + return -EFAULT; + } + + my_fifo_dev->rp += read_cont; + + if(my_fifo_dev->rp == my_fifo_dev->fifo_end) + my_fifo_dev->rp = my_fifo_dev->fifo_begain; + + up(&my_fifo_dev->sem); + + wake_up_interruptible(&my_fifo_dev->writeq); + + + return read_cont; +} + +/* How much space is free? */ +static int spacefree(struct my_fifo_dev *dev) +{ + if (dev->rp == dev->wp) + return dev->fifo_size - 1; + return ((dev->rp + dev->fifo_size - dev->wp) % dev->fifo_size) - 1; +} + + +/* Wait for space for writing; caller must hold device semaphore. On + * error the semaphore will be released before returning. */ +static int getwritespace(struct my_fifo_dev *dev, struct file *filp) +{ + while (spacefree(dev) == 0) { /* full */ + DEFINE_WAIT(wait); + + up(&dev->sem); + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + prepare_to_wait(&dev->writeq, &wait, TASK_INTERRUPTIBLE); + if (spacefree(dev) == 0){ + //printk(KERN_INFO "[ KERN_INFO ] rp:0x%p,wp:0x%p\n",dev->rp,dev->wp); + //printk(KERN_INFO "[ KERN_INFO ] waiting...\n"); + schedule(); + //printk(KERN_INFO "[ KERN_INFO ] wake up!\n"); + } + finish_wait(&dev->writeq, &wait); + if (signal_pending(current)){ + printk(KERN_ERR "[ KERN_ERR ] signal_pending err\n"); + return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ + } + if (down_interruptible(&dev->sem)){ + printk(KERN_ERR "[ KERN_ERR ] down_interruptible err\n"); + return -ERESTARTSYS; + } + } + return 0; +} + + + +static ssize_t my_fifo_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt) +{ + struct my_fifo_dev *my_fifo_dev = filp->private_data; + uint32_t write_cont = 0; + uint32_t ret = 0; + + if (!(filp->f_mode & FMODE_WRITE)){ + printk(KERN_ERR "[ KERN_ERR ] write is not permitted\n"); + return -EPERM; + } + + if(down_interruptible(&my_fifo_dev->sem)){ + printk(KERN_ERR "[ KERN_ERR ] down_interruptible err\n"); + return -ERESTARTSYS; + } + + ret = getwritespace(my_fifo_dev,filp); + if(ret){ + printk(KERN_ERR "[ KERN_ERR ] getwritespace failed ret = %d\n",ret); + return ret; + } + + write_cont = min(spacefree(my_fifo_dev),cnt); + if(my_fifo_dev->rp <= my_fifo_dev->wp) + write_cont = min(write_cont, (my_fifo_dev->fifo_end - my_fifo_dev->wp)); + else + write_cont = min(write_cont, (my_fifo_dev->rp - my_fifo_dev->wp - 1)); + + if (copy_from_user(my_fifo_dev->wp, buf, write_cont)) { + up (&my_fifo_dev->sem); + return -EFAULT; + } + + my_fifo_dev->wp += write_cont; + if (my_fifo_dev->wp == my_fifo_dev->fifo_end) + my_fifo_dev->wp = my_fifo_dev->fifo_begain; /* wrapped */ + up(&my_fifo_dev->sem); + + wake_up_interruptible(&my_fifo_dev->readq); + + return write_cont; +} +static int my_fifo_release(struct inode *inode, struct file *filp) +{ + struct my_fifo_dev *my_fifo_dev = NULL; + + my_fifo_dev = filp->private_data; + + down(&my_fifo_dev->sem); + if (filp->f_mode & FMODE_READ) + my_fifo_dev->nreaders--; + if (filp->f_mode & FMODE_WRITE) + my_fifo_dev->nwriters--; + if (my_fifo_dev->nreaders + my_fifo_dev->nwriters == 0) { + kfree(my_fifo_dev->fifo_begain); + my_fifo_dev->fifo_begain = NULL; + } + up(&my_fifo_dev->sem); + return 0; + +} + +static struct file_operations my_fifo_fops = { +.owner = THIS_MODULE, +.open = my_fifo_open, +.read = my_fifo_read, +.write = my_fifo_write, +.release = my_fifo_release, +}; + + +static int __init my_fifo_init(void) +{ + uint32_t ret = 0; + printk(KERN_INFO "[ KERN_INFO ] my fifo Module Init start\n"); + + if(my_fifo_major){ + devno = MKDEV(my_fifo_major,my_fifo_minor); + ret = register_chrdev_region(devno, 1, "my_fifo"); + } + else{ + + ret = alloc_chrdev_region(&devno, 0, 1, "my_fifo"); + my_fifo_major = MAJOR(devno); + } + if(ret < 0){ + printk(KERN_ERR "[ KERN_ERR ] chrdev alloc region failed\n"); + goto devno_fail; + } + + printk(KERN_INFO "[ KERN_INFO ] devno is %u(0x%x)(major:%u,minor:%u)\n",devno,devno,my_fifo_major,my_fifo_minor); + + my_fifo_device_p = kmalloc(sizeof(struct my_fifo_dev), GFP_KERNEL); + if(!my_fifo_device_p){ + printk(KERN_ERR "[ KERN_ERR ] kmalloc failed!\n"); + goto kmalloc_fail; + } + memset(my_fifo_device_p, 0, sizeof(struct my_fifo_dev)); + + init_waitqueue_head(&(my_fifo_device_p->readq)); + init_waitqueue_head(&(my_fifo_device_p->writeq)); + sema_init(&my_fifo_device_p->sem,1); + + cdev_init(&my_fifo_device_p->my_cdev, &my_fifo_fops); + my_fifo_device_p->my_cdev.owner = THIS_MODULE; + ret = cdev_add (&my_fifo_device_p->my_cdev, devno, 1); + + if(ret){ + printk(KERN_ERR "[ KERN_ERR ] cdev_add failed!\n"); + goto cdev_add_fail; + } + + myclass = class_create(THIS_MODULE, "myclass"); + if(IS_ERR(myclass)) + { + printk(KERN_ERR" create class faild!/n"); + ret = -EBUSY; + goto class_create_fail; + } + + device_create(myclass,NULL,MKDEV(my_fifo_major,my_fifo_minor),NULL,"myfifo"); + + return 0; + +class_create_fail: + cdev_del(&my_fifo_device_p->my_cdev); +cdev_add_fail: + kfree(my_fifo_device_p); +kmalloc_fail: + unregister_chrdev_region(devno, 1); +devno_fail: + return ret; +} + +static void __exit my_fifo_exit(void) +{ + + device_destroy(myclass,MKDEV(my_fifo_major,my_fifo_minor)); + + class_destroy(myclass); + + cdev_del(&my_fifo_device_p->my_cdev); + + if(my_fifo_device_p->fifo_begain != NULL) + kfree(my_fifo_device_p->fifo_begain); + + kfree(my_fifo_device_p); + + if(!devno) + unregister_chrdev_region(devno, 1); + + printk("[ default ] my_fifo Module Exit\n"); +} + +module_init(my_fifo_init); +module_exit(my_fifo_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + diff --git a/my_first_module/char_dev.h b/my_first_module/char_dev.h new file mode 100644 index 0000000..d926aa6 --- /dev/null +++ b/my_first_module/char_dev.h @@ -0,0 +1,14 @@ +#ifndef _CHAR_DEV_H +#define _CHAR_DEV_H + +#ifndef MY_FIFO_MAJOR +#define MY_FIFO_MAJOR 0 +#endif + +#ifndef MY_FIFO_SIZE +#define MY_FIFO_SIZE 1024 +#endif + + + +#endif \ No newline at end of file diff --git a/my_first_module/char_dev.ko b/my_first_module/char_dev.ko new file mode 100644 index 0000000..60f58d7 Binary files /dev/null and b/my_first_module/char_dev.ko differ diff --git a/my_first_module/char_dev.mod.c b/my_first_module/char_dev.mod.c new file mode 100644 index 0000000..e1a0129 --- /dev/null +++ b/my_first_module/char_dev.mod.c @@ -0,0 +1,61 @@ +#include +#include +#include + +MODULE_INFO(vermagic, VERMAGIC_STRING); + +__visible struct module __this_module +__attribute__((section(".gnu.linkonce.this_module"))) = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +static const struct modversion_info ____versions[] +__used +__attribute__((section("__versions"))) = { + { 0xfa985410, __VMLINUX_SYMBOL_STR(module_layout) }, + { 0x51eafc8e, __VMLINUX_SYMBOL_STR(param_ops_int) }, + { 0x495be299, __VMLINUX_SYMBOL_STR(class_destroy) }, + { 0x4d0abe31, __VMLINUX_SYMBOL_STR(device_destroy) }, + { 0x7485e15e, __VMLINUX_SYMBOL_STR(unregister_chrdev_region) }, + { 0xbce370d, __VMLINUX_SYMBOL_STR(device_create) }, + { 0xb37da9a1, __VMLINUX_SYMBOL_STR(cdev_del) }, + { 0x3c8c7d13, __VMLINUX_SYMBOL_STR(__class_create) }, + { 0xfeb25d8b, __VMLINUX_SYMBOL_STR(cdev_add) }, + { 0xb6828306, __VMLINUX_SYMBOL_STR(cdev_init) }, + { 0x275ef902, __VMLINUX_SYMBOL_STR(__init_waitqueue_head) }, + { 0xcca6874c, __VMLINUX_SYMBOL_STR(kmem_cache_alloc) }, + { 0x208614a6, __VMLINUX_SYMBOL_STR(kmalloc_caches) }, + { 0x29537c9e, __VMLINUX_SYMBOL_STR(alloc_chrdev_region) }, + { 0xd8e484f0, __VMLINUX_SYMBOL_STR(register_chrdev_region) }, + { 0x12da5bb2, __VMLINUX_SYMBOL_STR(__kmalloc) }, + { 0x822d3e16, __VMLINUX_SYMBOL_STR(nonseekable_open) }, + { 0x67c2fa54, __VMLINUX_SYMBOL_STR(__copy_to_user) }, + { 0x344b7739, __VMLINUX_SYMBOL_STR(prepare_to_wait_event) }, + { 0xfa2a45e, __VMLINUX_SYMBOL_STR(__memzero) }, + { 0xfbc74f64, __VMLINUX_SYMBOL_STR(__copy_from_user) }, + { 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) }, + { 0xd85cd67e, __VMLINUX_SYMBOL_STR(__wake_up) }, + { 0x1000e51, __VMLINUX_SYMBOL_STR(schedule) }, + { 0x499cb58c, __VMLINUX_SYMBOL_STR(prepare_to_wait) }, + { 0xc8b57c27, __VMLINUX_SYMBOL_STR(autoremove_wake_function) }, + { 0x1cfb04fa, __VMLINUX_SYMBOL_STR(finish_wait) }, + { 0x1afae5e7, __VMLINUX_SYMBOL_STR(down_interruptible) }, + { 0xf7802486, __VMLINUX_SYMBOL_STR(__aeabi_uidivmod) }, + { 0xefd6cf06, __VMLINUX_SYMBOL_STR(__aeabi_unwind_cpp_pr0) }, + { 0x4be7fb63, __VMLINUX_SYMBOL_STR(up) }, + { 0x37a0cba, __VMLINUX_SYMBOL_STR(kfree) }, + { 0xf473ffaf, __VMLINUX_SYMBOL_STR(down) }, +}; + +static const char __module_depends[] +__used +__attribute__((section(".modinfo"))) = +"depends="; + + +MODULE_INFO(srcversion, "C443B2548BAD9F64F3CED0F"); diff --git a/my_first_module/char_dev.mod.o b/my_first_module/char_dev.mod.o new file mode 100644 index 0000000..93624f3 Binary files /dev/null and b/my_first_module/char_dev.mod.o differ diff --git a/my_first_module/char_dev.o b/my_first_module/char_dev.o new file mode 100644 index 0000000..9abf4dc Binary files /dev/null and b/my_first_module/char_dev.o differ diff --git a/my_first_module/modules.order b/my_first_module/modules.order new file mode 100644 index 0000000..33b19c6 --- /dev/null +++ b/my_first_module/modules.order @@ -0,0 +1 @@ +kernel//home/chao/code/module/my_first_module/char_dev.ko diff --git a/my_first_module/test/test_myfifo.c b/my_first_module/test/test_myfifo.c new file mode 100644 index 0000000..8708a9e --- /dev/null +++ b/my_first_module/test/test_myfifo.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + int fd = 0; + int i = 0; + char w_buff[64] = {0}; + + fd = open("/dev/myfifo",O_RDWR); + + + while(1) + { + memset(w_buff,0,64); + sprintf(w_buff,"%d,",i++); + write(fd,w_buff,strlen(w_buff)); + } + + return 0; +} diff --git a/my_first_module/test/test_myfifo_r.c b/my_first_module/test/test_myfifo_r.c new file mode 100644 index 0000000..89481d7 --- /dev/null +++ b/my_first_module/test/test_myfifo_r.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + int fd = 0; + int i = 20; + char r_buff[64] = {0}; + + fd = open("/dev/myfifo",O_RDWR); + + + while(i) + { + memset(r_buff,0,64); + read(fd,r_buff,64); + printf("%s",r_buff); + i--; + } + + return 0; +} diff --git a/my_first_module/test/test_r b/my_first_module/test/test_r new file mode 100644 index 0000000..2eb7ef6 Binary files /dev/null and b/my_first_module/test/test_r differ diff --git a/my_first_module/test/test_w b/my_first_module/test/test_w new file mode 100644 index 0000000..c332a3c Binary files /dev/null and b/my_first_module/test/test_w differ diff --git a/parametermodule/Makefile b/parametermodule/Makefile new file mode 100644 index 0000000..58446e7 --- /dev/null +++ b/parametermodule/Makefile @@ -0,0 +1,18 @@ +KERNEL_DIR=../../ebf-buster-linux/build_image/build + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +obj-m := parametermodule.o calculation.o + +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean copy + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + +copy: + sudo cp *.ko /home/embedfire/workdiri \ No newline at end of file diff --git a/parametermodule/calculation.c b/parametermodule/calculation.c new file mode 100644 index 0000000..1bd7b63 --- /dev/null +++ b/parametermodule/calculation.c @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "calculation.h" + +static int __init calculation_init(void) + { + printk(KERN_ALERT "calculation init!\n"); + printk(KERN_ALERT "itype+1 = %d, itype-1 = %d\n", my_add(itype,1), my_sub(itype,1)); + return 0; + } + + static void __exit calculation_exit(void) +{ + printk(KERN_ALERT "calculation exit!\n"); +} + + +module_init(calculation_init); +module_exit(calculation_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("embedfire "); +MODULE_DESCRIPTION("calculation module"); +MODULE_ALIAS("calculation_module"); \ No newline at end of file diff --git a/parametermodule/calculation.h b/parametermodule/calculation.h new file mode 100644 index 0000000..8927123 --- /dev/null +++ b/parametermodule/calculation.h @@ -0,0 +1,9 @@ +#ifndef __CALCULATION_H__ +#define __CALCULATION_H__ + +extern int itype; + +int my_add(int a, int b); +int my_sub(int a, int b); + +#endif \ No newline at end of file diff --git a/parametermodule/parametermodule.c b/parametermodule/parametermodule.c new file mode 100644 index 0000000..db69466 --- /dev/null +++ b/parametermodule/parametermodule.c @@ -0,0 +1,54 @@ +#include +#include +#include + +static int itype=0; +module_param(itype,int,0); + +static bool btype=0; +module_param(btype,bool,0700); + +static char ctype=0; +module_param(ctype,byte,0); + +static char *stype=0; +module_param(stype,charp,0644); + +static int __init param_init(void) + { + printk(KERN_ALERT "param init!\n"); + printk(KERN_ALERT "itype=%d\n",itype); + printk(KERN_ALERT "btype=%d\n",btype); + printk(KERN_ALERT "ctype=%d\n",ctype); + printk(KERN_ALERT "stype=%s\n",stype); + return 0; +} + +static void __exit param_exit(void) +{ + printk(KERN_ALERT "module exit!\n"); +} + +EXPORT_SYMBOL(itype); + + int my_add(int a, int b) + { + return a+b; + } + +EXPORT_SYMBOL(my_add); + +int my_sub(int a, int b) + { + return a-b; + } + +EXPORT_SYMBOL(my_sub); + +module_init(param_init); +module_exit(param_exit); + +MODULE_LICENSE("GPL2"); +MODULE_AUTHOR("embedfire "); +MODULE_DESCRIPTION("module_param"); +MODULE_ALIAS("module_param"); \ No newline at end of file diff --git a/platform_device/Makefile b/platform_device/Makefile new file mode 100644 index 0000000..768323e --- /dev/null +++ b/platform_device/Makefile @@ -0,0 +1,30 @@ + +ifeq ($(DEBUG),y) + DEBFLAGS = -O -g -DDEBUG +endif + +EXTRA_CFLAGS += $(DEBFLAGS) + +KERNEL_DIR=../../ebf_6ull_linux + +ARCH=arm +CROSS_COMPILE=arm-linux-gnueabihf- +export ARCH CROSS_COMPILE + +#xxx-objs := main.o pipe.o access.o +obj-m := plat_dev.o plat_drv.o +all: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules + +.PHONE:clean + +clean: + $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean + + +#depend .depend dep: +# $(CC) $(EXTRA_CFLAGS) -M *.c > .depend + +#ifeq (.depend,$(wildcard .depend)) +#include .depend +#endif \ No newline at end of file diff --git a/platform_device/plat_dev.c b/platform_device/plat_dev.c new file mode 100644 index 0000000..14c24eb --- /dev/null +++ b/platform_device/plat_dev.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CCM_CCGR1 0x020C406C //时钟控制寄存器 +#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 0x020E006C //GPIO1_04复用功能选择寄存器 +#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04 0x020E02F8 //PAD属性设置寄存器 +#define GPIO1_GDIR 0x0209C004 //GPIO方向设置寄存器(输入或输出) +#define GPIO1_DR 0x0209C000 //GPIO输出状态寄存器 + +#define CCM_CCGR3 0x020C4074 +#define GPIO4_GDIR 0x020A8004 +#define GPIO4_DR 0x020A8000 + +#define IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO020 0x020E01E0 +#define IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO020 0x020E046C + +#define IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO019 0x020E01DC +#define IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO019 0x020E0468 + + +static int led_cdev_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static struct resource rled_resource[] = { + [0] = DEFINE_RES_MEM(GPIO1_DR, 4), + [1] = DEFINE_RES_MEM(GPIO1_GDIR, 4), + [2] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04, 4), + [3] = DEFINE_RES_MEM(CCM_CCGR1, 4), + [4] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04, 4), +}; + +unsigned int rled_hwinfo[2] = { 4, 26 }; + +static struct platform_device rled_pdev = { + .name = "led_pdev", + .id = 0, + .num_resources = ARRAY_SIZE(rled_resource), + .resource = rled_resource, + .dev = { + .release = led_cdev_release, + .platform_data = rled_hwinfo, + }, + +}; + +static struct resource gled_resource[] = { + [0] = DEFINE_RES_MEM(GPIO4_DR, 4), + [1] = DEFINE_RES_MEM(GPIO4_GDIR, 4), + [2] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO020, 4), + [3] = DEFINE_RES_MEM(CCM_CCGR1, 4), + [4] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO020, 4), +}; + +unsigned int gled_hwinfo[2] = { 20, 12 }; + + +static struct platform_device gled_pdev = { + .name = "led_pdev", + .id = 1, + .num_resources = ARRAY_SIZE(gled_resource), + .resource = gled_resource, + .dev = { + .release = led_cdev_release, + .platform_data = gled_hwinfo, + }, + +}; + + +static struct resource bled_resource[] = { + [0] = DEFINE_RES_MEM(GPIO4_DR, 4), + [1] = DEFINE_RES_MEM(GPIO4_GDIR, 4), + [2] = DEFINE_RES_MEM(IOMUXC_SW_MUX_CTL_PAD_GPIO4_IO019, 4), + [3] = DEFINE_RES_MEM(CCM_CCGR1, 4), + [4] = DEFINE_RES_MEM(IOMUXC_SW_PAD_CTL_PAD_GPIO4_IO019, 4), +}; + + +unsigned int bled_hwinfo[2] = { 19, 12 }; + + +static struct platform_device bled_pdev = { + .name = "led_pdev", + .id = 2, + .num_resources = ARRAY_SIZE(bled_resource), + .resource = bled_resource, + .dev = { + .release = led_cdev_release, + .platform_data = bled_hwinfo, + }, + +}; + + + + +static __init int led_pdev_init(void) +{ + printk("pdev init\n"); + platform_device_register(&rled_pdev); + platform_device_register(&gled_pdev); + platform_device_register(&bled_pdev); + return 0; + +} +module_init(led_pdev_init); + + +static __exit void led_pdev_exit(void) +{ + printk("pdev exit\n"); + platform_device_unregister(&bled_pdev); + platform_device_unregister(&gled_pdev); + platform_device_unregister(&rled_pdev); +} +module_exit(led_pdev_exit); + + +MODULE_AUTHOR("liuchao"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("the example for platform driver"); + + + diff --git a/platform_device/plat_drv.c b/platform_device/plat_drv.c new file mode 100644 index 0000000..6b82272 --- /dev/null +++ b/platform_device/plat_drv.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct led_chrdev { + unsigned int __iomem *va_dr; + unsigned int __iomem *va_gdir; + unsigned int __iomem *va_iomuxc_mux; + unsigned int __iomem *va_ccm_ccgrx; + unsigned int __iomem *va_iomux_pad; + unsigned int led_pin; + unsigned int clock_offset; + struct cdev dev; + dev_t my_devno; +}; + +static struct class *led_class = NULL; + +static int led_open(struct inode *inode, struct file *filp) +{ + uint32_t val = 0; + struct led_chrdev *my_led_dev = NULL; + + printk("led open %d:%d...\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev)); + my_led_dev = container_of(inode->i_cdev, struct led_chrdev, dev); + + filp->private_data = my_led_dev; + + val = ioread32(my_led_dev->va_ccm_ccgrx); + val &= ~(3 << my_led_dev->clock_offset); + val |= 3 << my_led_dev->clock_offset; + + iowrite32(val, my_led_dev->va_ccm_ccgrx); + iowrite32(5, my_led_dev->va_iomuxc_mux); + iowrite32(0x1F838, my_led_dev->va_iomux_pad); + + val = ioread32(my_led_dev->va_gdir); + val &= ~(1 << my_led_dev->led_pin); + val |= (1 << my_led_dev->led_pin); + iowrite32(val, my_led_dev->va_gdir); + + val = ioread32(my_led_dev->va_dr); + val |= (0x01 << my_led_dev->led_pin); + iowrite32(val, my_led_dev->va_dr); + + return 0; +} + +static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) +{ + return cnt; +} + +static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt) +{ + uint32_t val = 0; + uint32_t ret = 0; + struct led_chrdev *my_led_dev = NULL; + + printk("led write...\n"); + my_led_dev = filp->private_data; + + kstrtoul_from_user(buf,cnt,10,&ret); + + //printk("ret = %u\n",ret); + + val = ioread32(my_led_dev->va_dr); + + if (ret) + val &= ~(0x01 << my_led_dev->led_pin); + else + val |= (0x01 << my_led_dev->led_pin); + + iowrite32(val, my_led_dev->va_dr); + *offt += cnt; + + return cnt; + +} + +static int led_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static struct file_operations led_fops = { +.owner = THIS_MODULE, +.open = led_open, +.read = led_read, +.write = led_write, +.release = led_release, +}; + +static struct platform_device_id led_pdev_ids[] = { + {.name = "led_pdev"}, + {} +}; +MODULE_DEVICE_TABLE(platform, led_pdev_ids); + +int led_plat_probe(struct platform_device *pdev) +{ + uint32_t val = 0; + int ret = 0; + dev_t devno = 0; + struct led_chrdev *my_led_dev = NULL; + + unsigned int *led_hwinfo; + + + struct resource *mem_dr; + struct resource *mem_gdir; + struct resource *mem_iomuxc_mux; + struct resource *mem_ccm_ccgrx; + struct resource *mem_iomux_pad; + + led_hwinfo = dev_get_platdata(&(pdev->dev)); + + mem_dr = platform_get_resource(pdev,IORESOURCE_MEM,0); + mem_gdir = platform_get_resource(pdev,IORESOURCE_MEM,1); + mem_iomuxc_mux = platform_get_resource(pdev,IORESOURCE_MEM,2); + mem_ccm_ccgrx = platform_get_resource(pdev,IORESOURCE_MEM,3); + mem_iomux_pad = platform_get_resource(pdev,IORESOURCE_MEM,4); + + my_led_dev = devm_kzalloc(&(pdev->dev),sizeof(struct led_chrdev), GFP_KERNEL); + if(!my_led_dev) + return -ENOMEM; + + my_led_dev->led_pin = led_hwinfo[0]; + my_led_dev->clock_offset = led_hwinfo[1]; + + my_led_dev->va_ccm_ccgrx = ioremap(mem_ccm_ccgrx->start,4); + my_led_dev->va_dr = ioremap(mem_dr->start,4); + my_led_dev->va_gdir = ioremap(mem_gdir->start,4); + my_led_dev->va_iomuxc_mux = ioremap(mem_iomuxc_mux->start,4); + my_led_dev->va_iomux_pad = ioremap(mem_iomux_pad->start,4); + + ret = alloc_chrdev_region(&devno, pdev->id, 1, "led"); + if(ret < 0){ + printk(KERN_ERR "chrdev alloc region failed\n"); + return ret; + } + printk(KERN_INFO "devno is %u(major:%u,minor:%u)\n",devno,MAJOR(devno), MINOR(devno)); + my_led_dev->my_devno = devno; + + cdev_init(&my_led_dev->dev, &led_fops); + my_led_dev->dev.owner = THIS_MODULE; + + ret = cdev_add(&my_led_dev->dev, devno, 1); + if(ret < 0){ + printk(KERN_ERR "chrdev add failed\n"); + goto add_err; + } + device_create(led_class,NULL,devno,NULL,"led_%s",(pdev->id == 0)?"red":((pdev->id == 1)? "green":"blue")); + + platform_set_drvdata(pdev, my_led_dev); + + return 0; + +add_err: + unregister_chrdev_region(my_led_dev, 1); + return ret; + +} + +int led_plat_remove(struct platform_device *pdev) +{ + dev_t cur_dev; + struct led_chrdev *my_led_dev = platform_get_drvdata(pdev); + + printk("led platform driver remove\n"); + + iounmap(my_led_dev->va_ccm_ccgrx); + iounmap(my_led_dev->va_dr); + iounmap(my_led_dev->va_gdir); + iounmap(my_led_dev->va_iomuxc_mux); + iounmap(my_led_dev->va_iomux_pad); + cur_dev = my_led_dev->my_devno; + cdev_del(&my_led_dev->dev); + device_destroy(led_class, cur_dev); + unregister_chrdev_region(cur_dev, 1); + + return 0; +} + +static struct platform_driver led_pdrv = { + .probe = led_plat_probe, + .remove = led_plat_remove, + .driver.name = "led_pdev", + .id_table = led_pdev_ids, +}; + + +static int __init led_plat_drv_init(void) +{ + printk("led platform driver init\n"); + + + led_class = class_create(THIS_MODULE, "led_class"); + if(IS_ERR(led_class)) + { + printk(KERN_ERR" create class faild!/n"); + return -EBUSY; + } + platform_driver_register(&led_pdrv); + + return 0; +} + +static void __exit led_plat_drv_exit(void) +{ + + printk("led platform driver exit\n"); + + platform_driver_unregister(&led_pdrv); + class_destroy(led_class); +} + + +module_init(led_plat_drv_init); +module_exit(led_plat_drv_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("liuchao"); + + +