Ubuntu 18.04
通用型 I/O module source code (Makefile, giodrv.h, giodrv.c),測試程式顯示 Super I/O temperature sensor 温度 (Fintek F71889A):
# Makefile KVERSION := $(shell uname -r) obj-m := giodrv.o all: $(MAKE) -C /lib/modules/$(KVERSION)/build M=${PWD} modules clean: $(MAKE) -C /lib/modules/$(KVERSION)/build M=${PWD} clean
// giodrv.h #ifndef _GIODRV_H_ #define MSG(format, arg...) printk(KERN_INFO "giodrv: " format "\n", ## arg) #include <linux/ioctl.h> #define DEVICE_NAME "giodrv" #define DEVICE_IOCTLID 0xD0 struct ioctl_arg { unsigned int port; unsigned int val; unsigned int size; }; #define IOCTL_IOW _IOW(DEVICE_IOCTLID, 0, struct ioctl_arg) #define IOCTL_IOR _IOR(DEVICE_IOCTLID, 1, struct ioctl_arg) #define IOCTL_IO_REQUEST _IOR(DEVICE_IOCTLID, 2, struct ioctl_arg) #define IOCTL_IO_RELEASE _IOR(DEVICE_IOCTLID, 3, struct ioctl_arg) #endif
//giodrv.c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> //#include <asm/uaccess.h> #include <linux/uaccess.h> #include "giodrv.h" #define DRIVER_NAME "giodrv" #define CLASS_NAME "giodrv" static unsigned int chrdev_major = 0; static unsigned int num_of_dev = 1; static struct cdev chrdev_cdev; static struct class *chrdev_class; struct dev_data { unsigned char val; rwlock_t lock; }; void iow(unsigned int port, unsigned int val, unsigned int size) { switch (size) { case 1: outb(val, port); break; case 2: outw(val, port); break; case 4: outl(val, port); break; } } unsigned ior(unsigned int port, unsigned int size) { unsigned int value = 0; switch (size) { case 1: value = inb(port); break; case 2: value = inw(port); break; case 4: value = inl(port); break; } return value; } static long dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct dev_data *dev = filp->private_data; struct ioctl_arg data; int retval = 0; memset(&data, 0, sizeof(data)); switch (cmd) { case IOCTL_IO_REQUEST: if (copy_from_user(&data, (int __user *)arg, sizeof(data))) { retval = -1; goto done; } if (request_region(data.port, data.size, DRIVER_NAME) == NULL) { retval = -1; goto done; } break; case IOCTL_IO_RELEASE: if (copy_from_user(&data, (int __user *)arg, sizeof(data))) { retval = -1; goto done; } release_region(data.port, data.size); break; case IOCTL_IOW: if (copy_from_user(&data, (int __user *)arg, sizeof(data))) { retval = -1; goto done; } write_lock(&dev->lock); MSG("IOW port:0x%X val:0x%X size:%d", data.port, data.val, data.size); iow(data.port, data.val, data.size); write_unlock(&dev->lock); break; case IOCTL_IOR: if (copy_from_user(&data, (int __user *)arg, sizeof(data))) { retval = -1; goto done; } read_lock(&dev->lock); MSG("IOR port:0x%X size:%d", data.port, data.size); data.val = ior(data.port, data.size); MSG("Value: %x\n", data.val); if (copy_to_user((int __user *)arg, &data, sizeof(data))) { read_unlock(&dev->lock); retval = -1; goto done; } read_unlock(&dev->lock); break; default: return -EFAULT; } done: return (retval); } /**************************************************/ int dev_release(struct inode *inode, struct file *filp) { module_put(THIS_MODULE); if (filp->private_data) { kfree(filp->private_data); filp->private_data = NULL; } return 0; }; int dev_open(struct inode *inode, struct file *filp) { struct dev_data *dev = filp->private_data; try_module_get(THIS_MODULE); dev = kmalloc(sizeof(struct dev_data), GFP_KERNEL); if (dev == NULL) { return -ENOMEM; } rwlock_init(&dev->lock); dev->val = 0xFF; filp->private_data = dev; return 0; }; struct file_operations card_fops = { .owner = THIS_MODULE, .open = dev_open, .release = dev_release, .unlocked_ioctl = dev_ioctl, }; static char* devnode(struct device *dev, umode_t *mode) { if (!mode) return NULL; if (dev->devt == MKDEV(chrdev_major, 0) || dev->devt == MKDEV(chrdev_major, 2)) *mode = 0666; return NULL; } static int init_giodrv_module(void) { dev_t dev = MKDEV(chrdev_major, 0); int alloc_ret = 0; int cdev_ret = 0; alloc_ret = alloc_chrdev_region(&dev, 0, num_of_dev, DRIVER_NAME); if (alloc_ret) goto error; chrdev_major = MAJOR(dev); cdev_init(&chrdev_cdev, &card_fops); cdev_ret = cdev_add(&chrdev_cdev, dev, num_of_dev); if (cdev_ret) goto error; chrdev_class = class_create(THIS_MODULE, CLASS_NAME); //Name to be displayed inside /sys/class/ when driver is loaded. if (IS_ERR(chrdev_class)) goto error; chrdev_class->devnode = devnode; //Set device file (/dev/giodrv) permission to 0666 on creation device_create(chrdev_class, NULL, dev, NULL, DEVICE_NAME); //device file to be created under /dev. printk(KERN_ALERT "%s module(major: %d) installed.\n", DRIVER_NAME, chrdev_major); printk(KERN_ALERT "%s: Class /sys/class/%s created.\n", DRIVER_NAME, CLASS_NAME); printk(KERN_ALERT "%s: Device /dev/%s created.\n", DRIVER_NAME, DEVICE_NAME); return 0; error: if (cdev_ret == 0) cdev_del(&chrdev_cdev); if (alloc_ret == 0) unregister_chrdev_region(dev, num_of_dev); return -1; } static void exit_giodrv_module(void) { dev_t dev = MKDEV(chrdev_major, 0); device_destroy(chrdev_class, dev); class_destroy(chrdev_class); cdev_del(&chrdev_cdev); unregister_chrdev_region(dev, num_of_dev); printk(KERN_ALERT "%s driver removed.\n", DRIVER_NAME); } module_init(init_giodrv_module); module_exit(exit_giodrv_module); MODULE_LICENSE("GPL"); MODULE_AUTHOR("RayKuo's blog"); MODULE_DESCRIPTION("Generic I/O Driver"); MODULE_VERSION("1.0"); MODULE_INFO(intree, "Y"); //fix err: "giodrv: loading out-of-tree module taints kernel."
測試程式 (Makefile, test.c):
# Makefile all: gcc test.c -o test clean: rm test
// test.c #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include "../drv/giodrv.h" unsigned int isa_ior(int devfd, unsigned int port, unsigned reg) { struct ioctl_arg data; data.port = port; data.val = reg; data.size = 1; ioctl(devfd, IOCTL_IOW, &data); data.port = port+1; data.size = 1; data.val = 0; ioctl(devfd, IOCTL_IOR, &data); return data.val; } int main(int argc, char *argv[]) { struct ioctl_arg data; int devfd; devfd = open("/dev/" DEVICE_NAME, O_RDWR); if (devfd == -1) { printf("Can't open /dev/%s\n", DEVICE_NAME); return -1; } data.port=0x295; data.size = 2; //295,296 if (ioctl(devfd, IOCTL_IO_REQUEST, &data) == -1) { printf("Port 0x%x request fail\n", data.port); return -1; } printf("SIO Temp1: %d \n", isa_ior(devfd, 0x295, 0x72)); printf("SIO Temp2: %d \n", isa_ior(devfd, 0x295, 0x74)); printf("SIO Temp2: %d \n", isa_ior(devfd, 0x295, 0x76)); data.port=0x295; data.size = 2; //295,296 ioctl(devfd, IOCTL_IO_RELEASE, &data); printf("Done.\n"); close(devfd); return 0; }