├── README.md ├── chardev.h ├── main.c ├── custom_pci_dev.c └── custom_qemu_device_driver.c /README.md: -------------------------------------------------------------------------------- 1 | # cpcidev_pci 2 | code to emulate a custom pcidevice in QEMU (qemu mod, lkm, and userspace app) 3 | -------------------------------------------------------------------------------- /chardev.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARDEV_H 2 | #define CHARDEV_H 3 | 4 | #include 5 | 6 | /* The major device number. We can't rely on dynamic 7 | * registration any more, because ioctls need to know 8 | * it. */ 9 | #define MAJOR_NUM 251 10 | 11 | /* Set the message of the device driver */ 12 | #define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *) 13 | #define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, int) 14 | #define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int) 15 | 16 | #define IOCTL_GET_OP1 _IOR(MAJOR_NUM, 4, int *) 17 | #define IOCTL_GET_OP2 _IOR(MAJOR_NUM, 5, int *) 18 | #define IOCTL_GET_OPCODE _IOR(MAJOR_NUM, 6, int *) 19 | #define IOCTL_GET_RESULT _IOR(MAJOR_NUM, 7, int *) 20 | 21 | #define IOCTL_SET_OP1 _IOW(MAJOR_NUM, 8, int) 22 | #define IOCTL_SET_OP2 _IOW(MAJOR_NUM, 9, int ) 23 | #define IOCTL_SET_OPCODE _IOW(MAJOR_NUM, 10, int ) 24 | 25 | 26 | #define DEVICE_FILE_NAME "cpcidev_pci" 27 | 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * 5 | * 6 | * Userspace application to be used in conkunction with the 7 | * kernel module driver qemu_custom_device_driver.ko in order 8 | * to access the cpcidev_pci driver. 9 | * 10 | * @author: Mouzakitis Nikolaos (2023) 11 | * 12 | * 13 | * 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "chardev.h" 25 | 26 | #define BUF_SIZE 1024 27 | 28 | //global to printf from read/write functions. 29 | char buf[BUF_SIZE]; 30 | 31 | //read from the device file. 32 | int read_dev(void *ptr, int no_bytes, int no_items, FILE * fp, int addr) 33 | { 34 | int rv; 35 | 36 | //read from device file. 37 | fseek(fp, addr, 0); 38 | rv = fread(ptr, no_bytes, no_items, fp); 39 | 40 | printf("return value read(bytes): %d\n ",rv); 41 | for (int i = 0; i <4; i++) 42 | printf("%x ",buf[i]); 43 | 44 | printf("\n"); 45 | } 46 | 47 | int main() 48 | { 49 | FILE *fp; 50 | int rv; 51 | int op1, op2, opcode, result; 52 | 53 | void *ptr; 54 | int fd = open("/dev/cpcidev_pci", O_RDWR); 55 | 56 | printf("userspce program run.\n"); 57 | memset(buf, 0x0, BUF_SIZE); 58 | ptr = buf; 59 | if(fd == -1) 60 | { 61 | printf("error opening device /open()\n"); 62 | return -1; 63 | } 64 | 65 | printf("device opened success.\n"); 66 | printf("Perform addition 5+3\n"); 67 | 68 | op1 = 5; 69 | op2 = 3; 70 | opcode = 1; 71 | printf("set op1 %x\n",op1); 72 | ioctl(fd, 8, op1); 73 | printf("set op2 %x\n",op2); 74 | ioctl(fd, 9, op2); 75 | printf("set opcode %x\n",opcode); 76 | ioctl(fd, 10, opcode); 77 | 78 | printf("ioctl to get op1\n"); 79 | ioctl(fd, 4, &op1); 80 | printf("retrieved op1: %x\n", op1); 81 | 82 | printf("ioctl to get op2\n"); 83 | ioctl(fd, 5, &op2); 84 | printf("retrieved op1: %x\n", op2); 85 | 86 | printf("ioctl to get opcode\n"); 87 | ioctl(fd, 6, &opcode); 88 | printf("retrieved opcode: %x\n", opcode); 89 | 90 | //perform the addition. 91 | //read result. 92 | 93 | ioctl(fd, 7, &result); 94 | printf("retrived result: %x\n",result); 95 | 96 | printf("Perform 5-3\n"); 97 | opcode = 2; 98 | ioctl(fd, 10, opcode); 99 | 100 | ioctl(fd, 7, &result); 101 | printf("retrived result: %x\n",result); 102 | 103 | printf("Perform 5*3\n"); 104 | opcode = 3; 105 | ioctl(fd, 10, opcode); 106 | ioctl(fd, 7, &result); 107 | printf("retrived result: %x\n",result); 108 | 109 | 110 | close(fd); 111 | 112 | return 0; 113 | 114 | } 115 | -------------------------------------------------------------------------------- /custom_pci_dev.c: -------------------------------------------------------------------------------- 1 | 2 | #include "qemu/osdep.h" 3 | #include "qemu/units.h" 4 | #include "hw/pci/pci.h" 5 | #include "hw/hw.h" 6 | #include "hw/pci/msi.h" 7 | #include "qemu/timer.h" 8 | #include "qom/object.h" 9 | #include "qemu/main-loop.h" /* iothread mutex */ 10 | #include "qemu/module.h" 11 | #include "qapi/visitor.h" 12 | 13 | #define TYPE_PCI_CUSTOM_DEVICE "cpcidev" 14 | 15 | typedef struct CpcidevState CpcidevState; 16 | 17 | //This macro provides the instance type cast functions for a QOM type. 18 | DECLARE_INSTANCE_CHECKER(CpcidevState, CPCIDEV, TYPE_PCI_CUSTOM_DEVICE) 19 | 20 | //struct defining/descring the state 21 | //of the custom pci device. 22 | struct CpcidevState { 23 | PCIDevice pdev; 24 | MemoryRegion mmio; 25 | uint32_t op1; 26 | uint32_t op2; 27 | uint32_t opcode; 28 | uint32_t result; 29 | uint32_t error_calculation; //set when invalid opcode appears. 30 | }; 31 | 32 | static uint64_t cpcidev_mmio_read(void *opaque, hwaddr addr, unsigned size) 33 | { 34 | CpcidevState *cpcidev = opaque; 35 | uint64_t val = ~0ULL; 36 | printf("CPCIDEV: cpcidev_mmio_read() addr %lx \n", addr); 37 | printf("CPCIDEV: cpcidev_mmio_read() size %x \n", size); 38 | 39 | if (addr < 0x80 && size != 4) { 40 | return val; 41 | } 42 | 43 | if (addr >= 0x80 && size != 4 && size != 8) { 44 | return val; 45 | } 46 | 47 | switch (addr) { 48 | case 0x00: 49 | printf("addr 0x0\n"); 50 | //explain this one. 51 | val = 0x01234567u; 52 | break; 53 | case 0x10: 54 | printf("addr 0x10 return op1\n"); 55 | val = cpcidev->op1; 56 | break; 57 | case 0x14: 58 | printf("addr 0x14 return op2\n"); 59 | val = cpcidev->op2; 60 | break; 61 | case 0x18: 62 | printf("addr 0x18 return opcode\n"); 63 | val = cpcidev->opcode; 64 | break; 65 | case 0x30: 66 | printf("addr 0x30 return result\n"); 67 | 68 | //opcode for sum 69 | if(cpcidev->opcode == 0x1) { 70 | cpcidev->result = cpcidev->op1 + cpcidev->op2; 71 | cpcidev->error_calculation = 0x0; //correct operation 72 | } else if(cpcidev->opcode == 0x2) { //sub 73 | cpcidev->result = cpcidev->op1 - cpcidev->op2; 74 | cpcidev->error_calculation = 0x0; //correct operation 75 | } else if(cpcidev->opcode == 0x3) { //mult 76 | cpcidev->result = cpcidev->op1 * cpcidev->op2; 77 | cpcidev->error_calculation = 0x0; //correct operation 78 | } else { 79 | printf("INVALID OPCODE return 0xff;\n"); 80 | cpcidev->result = 0xff; 81 | cpcidev->error_calculation = 1; 82 | } 83 | 84 | val = cpcidev->result; 85 | 86 | break; 87 | } 88 | 89 | return val; 90 | } 91 | 92 | static void cpcidev_mmio_write(void *opaque, hwaddr addr, uint64_t val, 93 | unsigned size) 94 | { 95 | printf("CPCIDEV: cpcidev_mmio_write() addr %lx \n", addr); 96 | printf("CPCIDEV: cpcidev_mmio_write() val %lx \n", val); 97 | CpcidevState *cpcidev = opaque; 98 | 99 | if (addr < 0x80 && size != 4) { 100 | return; 101 | } 102 | 103 | if (addr >= 0x80 && size != 4 && size != 8) { 104 | return; 105 | } 106 | 107 | switch (addr) { 108 | case 0x10: 109 | printf("setting op1 to %lx \n", val); 110 | cpcidev->op1 = val; 111 | break; 112 | case 0x14: 113 | printf("setting op2 to %lx \n", val); 114 | cpcidev->op2 = val; 115 | break; 116 | case 0x18: 117 | printf("setting opcode to %lx \n", val); 118 | cpcidev->opcode = val; 119 | break; 120 | } 121 | } 122 | 123 | ///ops for the Memory Region. 124 | static const MemoryRegionOps cpcidev_mmio_ops = { 125 | .read = cpcidev_mmio_read, 126 | .write = cpcidev_mmio_write, 127 | .endianness = DEVICE_NATIVE_ENDIAN, 128 | .valid = { 129 | .min_access_size = 4, 130 | .max_access_size = 8, 131 | }, 132 | .impl = { 133 | .min_access_size = 4, 134 | .max_access_size = 8, 135 | }, 136 | 137 | }; 138 | 139 | //implementation of the realize function. 140 | static void pci_cpcidev_realize(PCIDevice *pdev, Error **errp) 141 | { 142 | CpcidevState *cpcidev = CPCIDEV(pdev); 143 | uint8_t *pci_conf = pdev->config; 144 | 145 | pci_config_set_interrupt_pin(pci_conf, 1); 146 | //supporting msi vectors ( 1 msi vector here of 64bit). 147 | //Make PCI device @dev MSI-capable. 148 | /* 149 | * Non-zero @offset puts capability MSI at that offset in PCI config 150 | * space. 151 | * @nr_vectors is the number of MSI vectors (1, 2, 4, 8, 16 or 32). 152 | * If @msi64bit, make the device capable of sending a 64-bit message 153 | * address. 154 | * If @msi_per_vector_mask, make the device support per-vector masking. 155 | * @errp is for returning errors. 156 | * Return 0 on success; set @errp and return -errno on error. */ 157 | if (msi_init(pdev, 0, 1, true, false, errp)) { 158 | return; 159 | } 160 | 161 | ///initial configuration of devices registers. 162 | cpcidev->op1 = 0x02; 163 | cpcidev->op2 = 0x04; 164 | cpcidev->opcode = 0xaa; 165 | cpcidev->result = 0xbb; 166 | cpcidev->error_calculation = 0x00; 167 | 168 | // Initialize an I/O memory region(cpcidev->mmio). 169 | // Accesses to this region will cause the callbacks 170 | // of the cpcidev_mmio_ops to be called. 171 | memory_region_init_io(&cpcidev->mmio, OBJECT(cpcidev), &cpcidev_mmio_ops, cpcidev, "cpcidev-mmio", 1 * MiB); 172 | // registering the pdev and all of the above configuration 173 | // (actually filling a PCI-IO region with our configuration. 174 | pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &cpcidev->mmio); 175 | } 176 | 177 | // uninitializing functions performed. 178 | static void pci_cpcidev_uninit(PCIDevice *pdev) 179 | { 180 | return; 181 | } 182 | 183 | 184 | ///initialization of the device 185 | static void cpcidev_instance_init(Object *obj) 186 | { 187 | return ; 188 | } 189 | 190 | static void cpcidev_class_init(ObjectClass *class, void *data) 191 | { 192 | DeviceClass *dc = DEVICE_CLASS(class); 193 | PCIDeviceClass *k = PCI_DEVICE_CLASS(class); 194 | 195 | //definition of realize func(). 196 | k->realize = pci_cpcidev_realize; 197 | //definition of uninit func(). 198 | k->exit = pci_cpcidev_uninit; 199 | k->vendor_id = PCI_VENDOR_ID_QEMU; 200 | k->device_id = 0xabcd; //our device id, 'abcd' hexadecimal 201 | k->revision = 0x10; 202 | k->class_id = PCI_CLASS_OTHERS; 203 | 204 | /** 205 | * set_bit - Set a bit in memory 206 | * @nr: the bit to set 207 | * @addr: the address to start counting from 208 | */ 209 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); 210 | } 211 | 212 | static void pci_custom_device_register_types(void) 213 | { 214 | static InterfaceInfo interfaces[] = { 215 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 216 | { }, 217 | }; 218 | static const TypeInfo custom_pci_device_info = { 219 | .name = TYPE_PCI_CUSTOM_DEVICE, 220 | .parent = TYPE_PCI_DEVICE, 221 | .instance_size = sizeof(CpcidevState), 222 | .instance_init = cpcidev_instance_init, 223 | .class_init = cpcidev_class_init, 224 | .interfaces = interfaces, 225 | }; 226 | //registers the new type. 227 | type_register_static(&custom_pci_device_info); 228 | } 229 | 230 | type_init(pci_custom_device_register_types) 231 | -------------------------------------------------------------------------------- /custom_qemu_device_driver.c: -------------------------------------------------------------------------------- 1 | #include /* put_user */ 2 | #include /* cdev_ */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "chardev.h" 10 | 11 | /* Each PCI device has 6 BAR IOs (base address register) as per the PCI spec. 12 | * 13 | * Each BAR corresponds to an address range that can be used to communicate with the PCI. 14 | * 15 | * Eech BAR is of one of the two types: 16 | * 17 | * - IORESOURCE_IO: must be accessed with inX and outX 18 | * - IORESOURCE_MEM: must be accessed with ioreadX and iowriteX 19 | * This is the saner method apparently, and what the edu device uses. 20 | * 21 | * The length of each region is defined BY THE HARDWARE, and communicated to software 22 | * via the configuration registers. 23 | * 24 | * The Linux kernel automatically parses the 64 bytes of standardized configuration registers for us. 25 | * 26 | * QEMU devices register those regions with: 27 | * 28 | * memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, 29 | * "edu-mmio", 1 << 20); 30 | * pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio); 31 | **/ 32 | #define BAR 0 33 | #define CDEV_NAME "cpcidev_pci" 34 | #define EDU_DEVICE_ID 0xabcd 35 | #define IO_IRQ_ACK 0x64 36 | #define IO_IRQ_STATUS 0x24 37 | #define QEMU_VENDOR_ID 0x1234 38 | 39 | MODULE_LICENSE("GPL"); 40 | 41 | static struct pci_device_id pci_ids[] = { 42 | //creating a struct pci_device_id matching the vendor id and device id. 43 | { PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), }, 44 | { 0, } 45 | }; 46 | 47 | //pci_ids need to be exported into user space to allow 48 | //hotplug and kernel modules know what module works with what 49 | //hardware device. 50 | MODULE_DEVICE_TABLE(pci, pci_ids); 51 | 52 | static int pci_irq; 53 | static int major; 54 | static struct pci_dev *pdev; 55 | static void __iomem *mmio; 56 | 57 | 58 | static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 59 | { 60 | int rv = 0; 61 | int *ip; 62 | int set; 63 | printk(KERN_INFO "dev_ioctl() cmd: %x arg: %x \n",cmd, arg); 64 | 65 | if(cmd == 4) 66 | { 67 | printk(KERN_INFO "retrieve op1.\n"); 68 | ip = (int *) arg; 69 | *ip = ioread32((void*)(mmio + 0x10)); 70 | printk(KERN_INFO "op1: %x\n",*ip); 71 | } 72 | 73 | if(cmd == 5) 74 | { 75 | printk(KERN_INFO "retrieve op2.\n"); 76 | ip = (int *) arg; 77 | *ip = ioread32((void*)(mmio + 0x14)); 78 | printk(KERN_INFO "op2: %x\n",*ip); 79 | } 80 | 81 | if(cmd == 6) 82 | { 83 | printk(KERN_INFO "retrieve opcode.\n"); 84 | ip = (int *) arg; 85 | *ip = ioread32((void*)(mmio + 0x18)); 86 | printk(KERN_INFO "opcode: %x\n",*ip); 87 | } 88 | 89 | if(cmd == 7) 90 | { 91 | printk(KERN_INFO "retrieve result.\n"); 92 | ip = (int *) arg; 93 | *ip = ioread32((void*)(mmio + 0x30)); 94 | printk(KERN_INFO "result: %x\n",*ip); 95 | } 96 | 97 | if(cmd == 8) 98 | { 99 | set = (int) arg; 100 | printk(KERN_INFO "set op1 %x\n",set); 101 | iowrite32(set, mmio + 0x10); 102 | } 103 | 104 | if(cmd == 9) 105 | { 106 | set = (int) arg; 107 | printk(KERN_INFO "set op2 %x\n",set); 108 | iowrite32(set, mmio + 0x14); 109 | } 110 | 111 | if(cmd == 10) 112 | { 113 | set = (int) arg; 114 | printk(KERN_INFO "set opcode %x\n",set); 115 | iowrite32(set, mmio + 0x18); 116 | } 117 | 118 | return rv; 119 | } 120 | 121 | 122 | static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off) 123 | { 124 | ssize_t ret; 125 | u32 kbuf; 126 | printk(KERN_INFO "CPCIDEV: read() len: %lx offset: %lx\n",len, *off); 127 | if (*off % 4 || len == 0) { 128 | ret = 0; 129 | } else { 130 | kbuf = ioread32(mmio + *off); 131 | if (copy_to_user(buf, (void *)&kbuf, 4)) { 132 | printk(KERN_ALERT "fault on copy_to_user\n"); 133 | ret = -EFAULT; 134 | } else { 135 | ret = 4; 136 | (*off)++; 137 | } 138 | } 139 | return ret; 140 | } 141 | 142 | static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off) 143 | { 144 | ssize_t ret; 145 | u32 kbuf; 146 | printk(KERN_INFO "CPCIDEV: write()\n"); 147 | 148 | ret = len; 149 | if (!(*off % 4)) { 150 | if (copy_from_user((void *)&kbuf, buf, 4) || len != 4) { 151 | ret = -EFAULT; 152 | } else { 153 | iowrite32(kbuf, mmio + *off); 154 | } 155 | } 156 | return ret; 157 | } 158 | 159 | static loff_t llseek(struct file *filp, loff_t off, int whence) 160 | { 161 | filp->f_pos = off; 162 | return off; 163 | } 164 | 165 | /* These fops are a bit daft since read and write interfaces don't map well to IO registers. 166 | * 167 | * One ioctl per register would likely be the saner option. But we are lazy. 168 | * 169 | * We use the fact that every IO is aligned to 4 bytes. Misaligned reads means EOF. */ 170 | static struct file_operations fops = { 171 | .owner = THIS_MODULE, 172 | .llseek = llseek, 173 | .read = read, 174 | .unlocked_ioctl = dev_ioctl, 175 | .write = write, 176 | }; 177 | 178 | static irqreturn_t irq_handler(int irq, void *dev) 179 | { 180 | int devi; 181 | irqreturn_t ret; 182 | u32 irq_status; 183 | 184 | devi = *(int *)dev; 185 | if (devi == major) { 186 | irq_status = ioread32(mmio + IO_IRQ_STATUS); 187 | pr_info("interrupt irq = %d dev = %d irq_status = %llx\n", 188 | irq, devi, (unsigned long long)irq_status); 189 | /* Must do this ACK, or else the interrupts just keeps firing. */ 190 | iowrite32(irq_status, mmio + IO_IRQ_ACK); 191 | ret = IRQ_HANDLED; 192 | } else { 193 | ret = IRQ_NONE; 194 | } 195 | return ret; 196 | } 197 | 198 | /** 199 | * Called just after insmod if the hardware device is connected, 200 | * not called otherwise. 201 | * 202 | * 0: all good 203 | * 1: failed 204 | */ 205 | static int pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 206 | { 207 | u8 val; 208 | 209 | pr_info("pci_probe\n"); 210 | 211 | major = register_chrdev(0, CDEV_NAME, &fops); 212 | 213 | pdev = dev; 214 | 215 | if (pci_enable_device(dev) < 0) { 216 | dev_err(&(pdev->dev), "pci_enable_device\n"); 217 | goto error; 218 | } 219 | 220 | if (pci_request_region(dev, BAR, "myregion0")) { 221 | dev_err(&(pdev->dev), "pci_request_region\n"); 222 | goto error; 223 | } 224 | mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR)); 225 | 226 | /* IRQ setup. */ 227 | pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &val); 228 | pci_irq = val; 229 | 230 | if (request_irq(pci_irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) { 231 | dev_err(&(dev->dev), "request_irq\n"); 232 | goto error; 233 | } 234 | 235 | /* Optional sanity checks. The PCI is ready now, all of this could also be called from fops. */ 236 | { 237 | unsigned i; 238 | 239 | /* Check that we are using MEM instead of IO. 240 | * 241 | * In QEMU, the type is defiened by either: 242 | * 243 | * - PCI_BASE_ADDRESS_SPACE_IO 244 | * - PCI_BASE_ADDRESS_SPACE_MEMORY 245 | */ 246 | if ((pci_resource_flags(dev, BAR) & IORESOURCE_MEM) != IORESOURCE_MEM) { 247 | dev_err(&(dev->dev), "pci_resource_flags\n"); 248 | goto error; 249 | } 250 | 251 | /* 1Mb, as defined by the "1 << 20" in QEMU's memory_region_init_io. Same as pci_resource_len. */ 252 | resource_size_t start = pci_resource_start(pdev, BAR); 253 | resource_size_t end = pci_resource_end(pdev, BAR); 254 | pr_info("length %llx\n", (unsigned long long)(end + 1 - start)); 255 | 256 | /* The PCI standardized 64 bytes of the configuration space, see LDD3. */ 257 | for (i = 0; i < 64u; ++i) { 258 | pci_read_config_byte(pdev, i, &val); 259 | pr_info("config %x %x\n", i, val); 260 | } 261 | pr_info("irq %x\n", pci_irq); 262 | 263 | /* Initial value of the IO memory. */ 264 | for (i = 0; i < 0x28; i += 4) { 265 | pr_info("io %x %x\n", i, ioread32((void*)(mmio + i))); 266 | } 267 | 268 | 269 | /* Read again values of the IO memory. */ 270 | for (i = 0; i < 0x28; i += 4) { 271 | pr_info("io %x %x\n", i, ioread32((void*)(mmio + i))); 272 | } 273 | 274 | 275 | 276 | } 277 | printk(KERN_INFO "pci_probe() returns\n"); 278 | return 0; 279 | error: 280 | return 1; 281 | } 282 | 283 | static void pci_remove(struct pci_dev *dev) 284 | { 285 | pr_info("pci_remove\n"); 286 | pci_release_region(dev, BAR); 287 | unregister_chrdev(major, CDEV_NAME); 288 | } 289 | 290 | static struct pci_driver pci_driver = { 291 | .name = "cpcidev_driver", 292 | .id_table = pci_ids, 293 | .probe = pci_probe, 294 | .remove = pci_remove, 295 | }; 296 | 297 | static int myinit(void) 298 | { 299 | //registration of the PCI driver. 300 | if (pci_register_driver(&pci_driver) < 0) { 301 | printk(KERN_INFO "CPCIDEV error registering the custom_qemu_device_driver\n"); 302 | return 1; 303 | } 304 | printk(KERN_INFO "CPCIDEV success on registering the custom_qemu_device_driver\n"); 305 | return 0; 306 | } 307 | 308 | static void myexit(void) 309 | { 310 | pci_unregister_driver(&pci_driver); 311 | } 312 | 313 | module_init(myinit); 314 | module_exit(myexit); 315 | --------------------------------------------------------------------------------