├── virtual_gpio_basic.h ├── Makefile ├── README.md ├── ivshmem-client.h ├── virtual_gpio_basic.c ├── vg_get_set.c ├── ivshmem-client.c └── LICENSE /virtual_gpio_basic.h: -------------------------------------------------------------------------------- 1 | #ifndef _VIRTUAL_GPIO_H_ 2 | #define _VIRTUAL_GPIO_H_ 3 | 4 | #define VIRTUAL_GPIO_DEV_NAME "ivshmem_gpio" 5 | #define VIRTUAL_GPIO_NR_GPIOS 32 6 | 7 | #define VIRTUAL_GPIO_DATA 0x00 8 | #define VIRTUAL_GPIO_OUT_EN 0x04 9 | #define VIRTUAL_GPIO_INT_EN 0x08 10 | #define VIRTUAL_GPIO_INT_ST 0x0c 11 | #define VIRTUAL_GPIO_INT_EOI 0x10 12 | #define VIRTUAL_GPIO_RISING 0x14 13 | #define VIRTUAL_GPIO_FALLING 0x18 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # file : Makefile 3 | # desc : Build linux device driver and userspace sources for 4 | # test with the QEMU ivshmem PCI device 5 | # 6 | 7 | usr = vg_get_set vg_guest_client 8 | OBJS := vg_get_set.o ivshmem-client.o 9 | krn = virtual_gpio_basic 10 | 11 | ifneq ($(KERNELRELEASE),) 12 | 13 | obj-m := $(krn).o 14 | 15 | else 16 | 17 | CFLAGS = `pkg-config --cflags glib-2.0` 18 | LDLIBS = `pkg-config --libs glib-2.0` 19 | 20 | KDIR ?= ${CURDIR}/../linux 21 | QEMU_DIR ?= ${CURDIR}/../qemu 22 | SYSROOT ?= ${CURDIR}/../initramfs 23 | CC = $(CROSS_COMPILE)gcc 24 | 25 | LINUXCONFIG += DEBUG=1 26 | 27 | vg_get_set: $(OBJS) 28 | gcc $(OBJS) -o vg_get_set $(LDLIBS) 29 | 30 | -include $(OBJS:.o=.d) 31 | 32 | %.o: %.c 33 | gcc -I. -I$(QEMU_DIR) -I$(QEMU_DIR)/include -I$(QEMU_DIR)/build -c $(CFLAGS) $*.c -o $*.o 34 | gcc -I. -I$(QEMU_DIR) -I$(QEMU_DIR)/include -I$(QEMU_DIR)/build -MM $(CFLAGS) $*.c > $*.d 35 | 36 | modules: 37 | $(MAKE) $(LINUXCONFIG) -C $(KDIR) M=$$PWD modules 38 | 39 | modules_install: 40 | $(MAKE) -C $(KDIR) M=$$PWD INSTALL_MOD_PATH=${SYSROOT} modules_install 41 | 42 | clean: 43 | $(MAKE) -C $(KDIR) M=$$PWD clean 44 | rm -f $(usr) 45 | 46 | all: modules vg_get_set vg_guest_client 47 | 48 | debug: CFLAGS += -DDEBUG -g 49 | debug: vg_get_set 50 | 51 | .PHONY : clean 52 | endif 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DESCRIPTION 2 | 3 | PCI virtual gpio with interrupts support for using together with ivshmem qemu 4 | 5 | See original articles for older versions: 6 | 7 | - http://maquefel.me/en/qemu-ivshmem-introducing-interrupt-capable-virtual-gpio-concept/ 8 | - http://maquefel.me/en/using-gpio-generic-and-irq_chip_generic-subsystems-for-gpio-driver/ 9 | 10 | # REQUIREMENTS 11 | 12 | - linux kernel 13 | - qemu 14 | 15 | This version is done for usage with 5.12 linux kernel and 6.0 qemu 16 | 17 | # COMPILATION 18 | 19 | KDIR={$PATH_TO_KERNEL_SOURCE_DIRECTORY} QEMU_DIR={$PATH_TO_QEMU_SOURCES_DIRECTORY} make all 20 | 21 | optional ARCH={$ARCH} 22 | optional CROSS_COMPILE={$CROSSCOMPILER_PREFIX} 23 | 24 | # QUICKSTART 25 | 26 | Use https://github.com/maquefel/kernel-bisect-template/tree/virtual_gpio project for quickstart. 27 | 28 | # USAGE 29 | 30 | I have dropped non-irq based version and switched to MSI vector usage. The modules simply won't load with error (it can be fixed easily if you really require that). 31 | 32 | See https://github.com/qemu/qemu/blob/master/docs/specs/ivshmem-spec.txt for additional info. 33 | 34 | ## WITH INTERRUPTS 35 | 36 | ``` 37 | host machine: 38 | $ ./qemu/build/contrib/ivshmem-server/ivshmem-server -F -v -l 1M -n 1 39 | $ qemu-system-x86_64 -cpu host -kernel build-linux/arch/x86/boot/bzImage -initrd initramfs.cpio.xz -nographic -append "nokaslr console=ttyS0 root=/dev/ram" -chardev socket,path=/tmp/ivshmem_socket,id=ivshmemid -device ivshmem-doorbell,chardev=ivshmemid,vectors=1 -enable-kvm 40 | 41 | guest # lsgpio 42 | ... 43 | GPIO chip: gpiochip4, "0000:00:04.0", 32 GPIO lines 44 | line 0: unnamed unused [input] 45 | line 1: unnamed unused [input] 46 | line 2: unnamed unused [input] 47 | line 3: unnamed unused [input] 48 | ... 49 | 50 | guest # /usr/bin/gpio-event-mon -n gpiochip4 -o 0 51 | 52 | host $ ./vg_get_set -p 6 -i 0 # get -p (peer id) from ivshmem-client 53 | main:346:: write 0=1 54 | main:349:: interrupt was enabled for 0 55 | main:353:: interrupt was raised for 0 56 | main:357:: rising edge interrupt was enabled for 0 and new value is high 1 57 | 58 | guest: 59 | No flags specified, listening on both rising and falling edges 60 | Monitoring line 0 on gpiochip4 61 | Initial line value: 0 62 | 63 | GPIO EVENT at 33533945213 on line 0 (1|1) rising edge 64 | GPIO EVENT at 39910880760 on line 0 (2|2) falling edge 65 | GPIO EVENT at 44507121043 on line 0 (3|3) rising edge 66 | ``` 67 | 68 | # COPYRIGHT 69 | 70 | GPL 71 | Nikita Shubin 72 | -------------------------------------------------------------------------------- /ivshmem-client.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 6WIND S.A., 2014 3 | * 4 | * This work is licensed under the terms of the GNU GPL, version 2 or 5 | * (at your option) any later version. See the COPYING file in the 6 | * top-level directory. 7 | */ 8 | 9 | #ifndef IVSHMEM_CLIENT_H 10 | #define IVSHMEM_CLIENT_H 11 | 12 | /** 13 | * This file provides helper to implement an ivshmem client. It is used 14 | * on the host to ask QEMU to send an interrupt to an ivshmem PCI device in a 15 | * guest. QEMU also implements an ivshmem client similar to this one, they both 16 | * connect to an ivshmem server. 17 | * 18 | * A standalone ivshmem client based on this file is provided for debug/test 19 | * purposes. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include "qemu/queue.h" 26 | #include "hw/misc/ivshmem.h" 27 | 28 | /** 29 | * Maximum number of notification vectors supported by the client 30 | */ 31 | #define IVSHMEM_CLIENT_MAX_VECTORS 64 32 | 33 | /** 34 | * Structure storing a peer 35 | * 36 | * Each time a client connects to an ivshmem server, it is advertised to 37 | * all connected clients through the unix socket. When our ivshmem 38 | * client receives a notification, it creates a IvshmemClientPeer 39 | * structure to store the infos of this peer. 40 | * 41 | * This structure is also used to store the information of our own 42 | * client in (IvshmemClient)->local. 43 | */ 44 | typedef struct IvshmemClientPeer { 45 | QTAILQ_ENTRY(IvshmemClientPeer) next; /**< next in list*/ 46 | int64_t id; /**< the id of the peer */ 47 | int vectors[IVSHMEM_CLIENT_MAX_VECTORS]; /**< one fd per vector */ 48 | unsigned vectors_count; /**< number of vectors */ 49 | } IvshmemClientPeer; 50 | 51 | typedef struct IvshmemClient IvshmemClient; 52 | 53 | /** 54 | * Typedef of callback function used when our IvshmemClient receives a 55 | * notification from a peer. 56 | */ 57 | typedef void (*IvshmemClientNotifCb)( 58 | const IvshmemClient *client, 59 | const IvshmemClientPeer *peer, 60 | unsigned vect, void *arg); 61 | 62 | /** 63 | * Structure describing an ivshmem client 64 | * 65 | * This structure stores all information related to our client: the name 66 | * of the server unix socket, the list of peers advertised by the 67 | * server, our own client information, and a pointer the notification 68 | * callback function used when we receive a notification from a peer. 69 | */ 70 | struct IvshmemClient { 71 | char unix_sock_path[PATH_MAX]; /**< path to unix sock */ 72 | int sock_fd; /**< unix sock filedesc */ 73 | int shm_fd; /**< shm file descriptor */ 74 | 75 | QTAILQ_HEAD(, IvshmemClientPeer) peer_list; /**< list of peers */ 76 | IvshmemClientPeer local; /**< our own infos */ 77 | 78 | IvshmemClientNotifCb notif_cb; /**< notification callback */ 79 | void *notif_arg; /**< notification argument */ 80 | 81 | bool verbose; /**< true to enable debug */ 82 | }; 83 | 84 | /** 85 | * Initialize an ivshmem client 86 | * 87 | * @client: A pointer to an uninitialized IvshmemClient structure 88 | * @unix_sock_path: The pointer to the unix socket file name 89 | * @notif_cb: If not NULL, the pointer to the function to be called when 90 | * our IvshmemClient receives a notification from a peer 91 | * @notif_arg: Opaque pointer given as-is to the notification callback 92 | * function 93 | * @verbose: True to enable debug 94 | * 95 | * Returns: 0 on success, or a negative value on error 96 | */ 97 | int ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path, 98 | IvshmemClientNotifCb notif_cb, void *notif_arg, 99 | bool verbose); 100 | 101 | /** 102 | * Connect to the server 103 | * 104 | * Connect to the server unix socket, and read the first initial 105 | * messages sent by the server, giving the ID of the client and the file 106 | * descriptor of the shared memory. 107 | * 108 | * @client: The ivshmem client 109 | * 110 | * Returns: 0 on success, or a negative value on error 111 | */ 112 | int ivshmem_client_connect(IvshmemClient *client); 113 | 114 | /** 115 | * Close connection to the server and free all peer structures 116 | * 117 | * @client: The ivshmem client 118 | */ 119 | void ivshmem_client_close(IvshmemClient *client); 120 | 121 | /** 122 | * Fill a fd_set with file descriptors to be monitored 123 | * 124 | * This function will fill a fd_set with all file descriptors 125 | * that must be polled (unix server socket and peers eventfd). The 126 | * function will not initialize the fd_set, it is up to the caller 127 | * to do this. 128 | * 129 | * @client: The ivshmem client 130 | * @fds: The fd_set to be updated 131 | * @maxfd: Must be set to the max file descriptor + 1 in fd_set. This value is 132 | * updated if this function adds a greater fd in fd_set. 133 | */ 134 | void ivshmem_client_get_fds(const IvshmemClient *client, fd_set *fds, 135 | int *maxfd); 136 | 137 | /** 138 | * Read and handle new messages 139 | * 140 | * Given a fd_set filled by select(), handle incoming messages from 141 | * server or peers. 142 | * 143 | * @client: The ivshmem client 144 | * @fds: The fd_set containing the file descriptors to be checked. Note 145 | * that file descriptors that are not related to our client are 146 | * ignored. 147 | * @maxfd: The maximum fd in fd_set, plus one. 148 | * 149 | * Returns: 0 on success, or a negative value on error 150 | */ 151 | int ivshmem_client_handle_fds(IvshmemClient *client, fd_set *fds, int maxfd); 152 | 153 | /** 154 | * Send a notification to a vector of a peer 155 | * 156 | * @client: The ivshmem client 157 | * @peer: The peer to be notified 158 | * @vector: The number of the vector 159 | * 160 | * Returns: 0 on success, or a negative value on error 161 | */ 162 | int ivshmem_client_notify(const IvshmemClient *client, 163 | const IvshmemClientPeer *peer, unsigned vector); 164 | 165 | /** 166 | * Send a notification to all vectors of a peer 167 | * 168 | * @client: The ivshmem client 169 | * @peer: The peer to be notified 170 | * 171 | * Returns: 0 on success, or a negative value on error (at least one 172 | * notification failed) 173 | */ 174 | int ivshmem_client_notify_all_vects(const IvshmemClient *client, 175 | const IvshmemClientPeer *peer); 176 | 177 | /** 178 | * Broadcast a notification to all vectors of all peers 179 | * 180 | * @client: The ivshmem client 181 | * 182 | * Returns: 0 on success, or a negative value on error (at least one 183 | * notification failed) 184 | */ 185 | int ivshmem_client_notify_broadcast(const IvshmemClient *client); 186 | 187 | /** 188 | * Search a peer from its identifier 189 | * 190 | * Return the peer structure from its peer_id. If the given peer_id is 191 | * the local id, the function returns the local peer structure. 192 | * 193 | * @client: The ivshmem client 194 | * @peer_id: The identifier of the peer structure 195 | * 196 | * Returns: The peer structure, or NULL if not found 197 | */ 198 | IvshmemClientPeer * 199 | ivshmem_client_search_peer(IvshmemClient *client, int64_t peer_id); 200 | 201 | /** 202 | * Dump information of this ivshmem client on stdout 203 | * 204 | * Dump the id and the vectors of the given ivshmem client and the list 205 | * of its peers and their vectors on stdout. 206 | * 207 | * @client: The ivshmem client 208 | */ 209 | void ivshmem_client_dump(const IvshmemClient *client); 210 | 211 | #endif /* IVSHMEM_CLIENT_H */ 212 | -------------------------------------------------------------------------------- /virtual_gpio_basic.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Driver for the QEMU ivshmem GPIO PCI device 4 | * 5 | * Copyright (c) 2021 Nikita Shubin 6 | * 7 | * derived from: 8 | * file : ne_ivshmem_basic_ldd.c 9 | * desc : demo linux device driver for the QEMU ivshmem PCI device 10 | * 11 | * notes: This is a skeleton version of "kvm_ivshmem.c" by 12 | * Cam Macdonell , Copyright 2009, GPLv2 13 | * See git://gitorious.org/nahanni/guest-code.git 14 | * 15 | * Siro Mugabi, nairobi-embedded.org 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #ifdef CONFIG_DEBUG_FS 32 | #include 33 | #endif 34 | 35 | #define VIRTUAL_GPIO_DEV_NAME "ivshmem_gpio" 36 | #define VIRTUAL_GPIO_NR_GPIOS 32 37 | 38 | #define VIRTUAL_GPIO_DATA 0x00 39 | #define VIRTUAL_GPIO_OUT_EN 0x04 40 | #define VIRTUAL_GPIO_INT_EN 0x08 41 | #define VIRTUAL_GPIO_INT_ST 0x0c 42 | #define VIRTUAL_GPIO_INT_EOI 0x10 43 | #define VIRTUAL_GPIO_RISING 0x14 44 | #define VIRTUAL_GPIO_FALLING 0x18 45 | 46 | #ifndef BITS_TO_BYTES 47 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) 48 | #endif 49 | 50 | static int nirqs = 32; 51 | module_param(nirqs, int, 0644); 52 | MODULE_PARM_DESC(nirqs, "number of interrupts to allocate"); 53 | 54 | /* 55 | * ============================================================ 56 | * BASIC STUFF 57 | * ============================================================ 58 | */ 59 | struct virtual_gpio { 60 | spinlock_t lock; 61 | struct pci_dev *pdev; 62 | /* gpiolib */ 63 | struct gpio_chip chip; 64 | /* data mmio region */ 65 | void __iomem *data_base_addr; 66 | 67 | /* irq handling */ 68 | unsigned int irq; 69 | struct irq_chip ic; 70 | }; 71 | 72 | static struct pci_device_id virtual_gpio_id_table[] = { 73 | { 0x1af4, 0x1110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 32 }, 74 | { 0 }, 75 | }; 76 | MODULE_DEVICE_TABLE(pci, virtual_gpio_id_table); 77 | 78 | /* relevant control register offsets */ 79 | enum { 80 | IntrMask = 0x00, /* Interrupt Mask */ 81 | IntrStatus = 0x04, /* Interrupt Status */ 82 | }; 83 | 84 | /* 85 | * ============================================================ 86 | * UTILITY STUFF 87 | * ============================================================ 88 | */ 89 | 90 | static inline struct virtual_gpio *to_virtual_gpio(struct gpio_chip *chip) 91 | { 92 | return container_of(chip, struct virtual_gpio, chip); 93 | } 94 | 95 | #ifdef CONFIG_DEBUG_FS 96 | static void virtual_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 97 | { 98 | struct virtual_gpio *vg = to_virtual_gpio(chip); 99 | struct gpio_chip *gc = &vg->chip; 100 | 101 | u32 outen, data; 102 | int gpio, is_out, i; 103 | 104 | outen = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_OUT_EN); 105 | 106 | gpio = vg->chip.base; 107 | 108 | data = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_DATA); 109 | 110 | is_out = 1; 111 | for (i = 0; i < vg->chip.ngpio; i++, gpio++) { 112 | seq_printf(s, " %s%d gpio-%-3d (%-12s) %s %s\n", 113 | vg->chip.label, i, gpio, 114 | gpiochip_is_requested(chip, i) ? : "", 115 | is_out ? "out" : "in ", 116 | (data & (1 << i)) ? "hi" : "lo"); 117 | } 118 | } 119 | #else 120 | #define virtual_gpio_dbg_show NULL 121 | #endif 122 | 123 | /* 124 | * ============================================================ 125 | * IRQ STUFF 126 | * ============================================================ 127 | */ 128 | static void virtual_gpio_interrupt(struct irq_desc *desc) 129 | { 130 | struct gpio_chip *gc = irq_desc_get_handler_data(desc); 131 | struct virtual_gpio *vg = gpiochip_get_data(gc); 132 | struct irq_chip *irqchip = irq_desc_get_chip(desc); 133 | unsigned long i, pending; 134 | 135 | chained_irq_enter(irqchip, desc); 136 | pending = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_ST); 137 | /* check if irq is really raised */ 138 | if (pending) 139 | for_each_set_bit(i, &pending, vg->chip.ngpio) 140 | generic_handle_irq(irq_find_mapping(vg->chip.irq.domain, i)); 141 | 142 | chained_irq_exit(irqchip, desc); 143 | } 144 | 145 | static void virtual_gpio_irq_ack(struct irq_data *d) 146 | { 147 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 148 | struct virtual_gpio *vg = to_virtual_gpio(gc); 149 | unsigned long flags; 150 | u8 nr = d->hwirq; 151 | u8 mask = 1 << nr; 152 | 153 | spin_lock_irqsave(&vg->lock, flags); 154 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EOI, mask); 155 | spin_unlock_irqrestore(&vg->lock, flags); 156 | } 157 | 158 | static void virtual_gpio_irq_mask(struct irq_data *d) 159 | { 160 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 161 | struct virtual_gpio *vg = to_virtual_gpio(gc); 162 | u8 mask; 163 | unsigned long flags; 164 | u8 nr = d->hwirq; 165 | 166 | spin_lock_irqsave(&vg->lock, flags); 167 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EN); 168 | mask &= ~(1 << nr); 169 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EN, mask); 170 | spin_unlock_irqrestore(&vg->lock, flags); 171 | } 172 | 173 | static void virtual_gpio_irq_unmask(struct irq_data *d) 174 | { 175 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 176 | struct virtual_gpio *vg = to_virtual_gpio(gc); 177 | u8 mask; 178 | unsigned long flags; 179 | u8 nr = d->hwirq; 180 | 181 | spin_lock_irqsave(&vg->lock, flags); 182 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EN); 183 | mask |= (1 << nr); 184 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EN, mask); 185 | spin_unlock_irqrestore(&vg->lock, flags); 186 | } 187 | 188 | /* 189 | * gpio_int_type1 controls whether the interrupt is level (0) or 190 | * edge (1) triggered, while gpio_int_type2 controls whether it 191 | * triggers on low/falling (0) or high/rising (1). 192 | */ 193 | static int virtual_gpio_irq_type(struct irq_data *d, unsigned int type) 194 | { 195 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 196 | struct virtual_gpio *vg = to_virtual_gpio(gc); 197 | unsigned long flags; 198 | int retval = 0; 199 | int offset = d->hwirq & (BIT(VIRTUAL_GPIO_NR_GPIOS) - 1); 200 | int port_mask = BIT(offset); 201 | u32 mask; 202 | 203 | dev_dbg(&vg->pdev->dev, "%s : irq=%u hwirq=%lu type=%u\n", __func__, d->irq, d->hwirq, type); 204 | 205 | gpio_direction_input(gc->base + d->hwirq); 206 | 207 | spin_lock_irqsave(&vg->lock, flags); 208 | switch (type) { 209 | case IRQ_TYPE_EDGE_RISING: 210 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_RISING); 211 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_RISING, mask | port_mask); 212 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_FALLING); 213 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_FALLING, mask & ~port_mask); 214 | break; 215 | case IRQ_TYPE_EDGE_FALLING: 216 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_FALLING); 217 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_FALLING, mask | port_mask); 218 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_RISING); 219 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_RISING, mask & ~port_mask); 220 | break; 221 | case IRQ_TYPE_EDGE_BOTH: 222 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_RISING); 223 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_RISING, mask | port_mask); 224 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_FALLING); 225 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_FALLING, mask | port_mask); 226 | break; 227 | default: 228 | retval = -EINVAL; 229 | goto end; 230 | } 231 | 232 | /* enable interrupt */ 233 | mask = gc->read_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EN); 234 | gc->write_reg(vg->data_base_addr + VIRTUAL_GPIO_INT_EN, mask | port_mask); 235 | end: 236 | spin_unlock_irqrestore(&vg->lock, flags); 237 | return retval; 238 | } 239 | 240 | /* ============================================================ 241 | * PROBE/REMOVE STUFF 242 | * ============================================================ 243 | */ 244 | static int virtual_gpio_setup(struct pci_dev *pdev, 245 | struct virtual_gpio *vg, 246 | int irq) 247 | { 248 | void __iomem *data = vg->data_base_addr + VIRTUAL_GPIO_DATA; 249 | void __iomem *dir = vg->data_base_addr + VIRTUAL_GPIO_OUT_EN; 250 | struct gpio_chip *gc = &vg->chip; 251 | struct device *dev = &pdev->dev; 252 | struct gpio_irq_chip *girq = &gc->irq; 253 | struct irq_chip *ic = &vg->ic; 254 | int err; 255 | 256 | gc->label = dev_name(&vg->pdev->dev); 257 | gc->owner = THIS_MODULE; 258 | gc->can_sleep = 0; // gpio never sleeps! 259 | gc->dbg_show = NULL; 260 | #ifdef CONFIG_DEBUG_FS 261 | gc->dbg_show = virtual_gpio_dbg_show; 262 | #endif 263 | 264 | err = bgpio_init(gc, dev, BITS_TO_BYTES(VIRTUAL_GPIO_NR_GPIOS), data, NULL, NULL, dir, NULL, 0); 265 | if (err) 266 | return err; 267 | 268 | ic->irq_ack = virtual_gpio_irq_ack; 269 | ic->irq_mask = virtual_gpio_irq_mask; 270 | ic->irq_unmask = virtual_gpio_irq_unmask; 271 | ic->irq_set_type = virtual_gpio_irq_type; 272 | girq->chip = ic; 273 | 274 | girq->parent_handler = virtual_gpio_interrupt; 275 | girq->num_parents = 1; 276 | girq->parents = devm_kcalloc(dev, girq->num_parents, 277 | sizeof(*girq->parents), 278 | GFP_KERNEL); 279 | if (!girq->parents) 280 | return -ENOMEM; 281 | 282 | girq->default_type = IRQ_TYPE_NONE; 283 | girq->handler = handle_edge_irq; 284 | girq->parents[0] = irq; 285 | 286 | vg->irq = irq; 287 | 288 | return devm_gpiochip_add_data(dev, gc, vg); 289 | } 290 | 291 | static int virtual_gpio_probe(struct pci_dev *pdev, 292 | const struct pci_device_id *pdev_id) 293 | { 294 | struct virtual_gpio *vg; 295 | struct device *dev = &pdev->dev; 296 | int nvecs; 297 | int err = 0; 298 | int irq; 299 | void __iomem *regs_base_addr; 300 | 301 | pr_err("%s : hello\n", __func__); 302 | 303 | vg = devm_kzalloc(&pdev->dev, sizeof(*vg), GFP_KERNEL); 304 | 305 | if (!vg) 306 | return -ENOMEM; 307 | 308 | pci_set_drvdata(pdev, vg); 309 | 310 | vg->pdev = pdev; 311 | spin_lock_init(&vg->lock); 312 | 313 | err = pcim_enable_device(pdev); 314 | if (err) 315 | return err; 316 | 317 | err = pcim_iomap_regions(pdev, BIT(2) | BIT(0), VIRTUAL_GPIO_DEV_NAME); 318 | if (err) { 319 | dev_err(dev, "I/O memory mapping error\n"); 320 | return err; 321 | } 322 | 323 | /* BAR2: data mmio region */ 324 | vg->data_base_addr = pcim_iomap_table(pdev)[2]; 325 | 326 | /* BAR0: control registers */ 327 | regs_base_addr = pcim_iomap_table(pdev)[0]; 328 | 329 | /* interrupts: set all masks */ 330 | iowrite32(0xffff, regs_base_addr + IntrMask); 331 | 332 | /* Release the IO mapping, since we already get the info from BAR0 */ 333 | pcim_iounmap_regions(pdev, BIT(0)); 334 | 335 | if (nirqs < 1) 336 | nirqs = 1; 337 | 338 | nvecs = pci_alloc_irq_vectors(pdev, 1, nirqs, PCI_IRQ_ALL_TYPES); 339 | if (nvecs < 0) 340 | return nvecs; 341 | 342 | dev_dbg(dev, "nvecs=%d\n", nvecs); 343 | 344 | irq = pci_irq_vector(pdev, 0); 345 | if (irq < 0) 346 | return irq; 347 | 348 | err = virtual_gpio_setup(pdev, vg, irq); 349 | if (err) 350 | return err; 351 | 352 | dev_info(dev, "loaded\n"); 353 | 354 | return 0; 355 | } 356 | 357 | static struct pci_driver virtual_gpio_pci_driver = { 358 | .name = VIRTUAL_GPIO_DEV_NAME, 359 | .id_table = virtual_gpio_id_table, 360 | .probe = virtual_gpio_probe, 361 | }; 362 | 363 | /* ============================================================ 364 | * MODULE INIT/EXIT 365 | * ============================================================ 366 | */ 367 | module_pci_driver(virtual_gpio_pci_driver); 368 | 369 | MODULE_LICENSE("GPL"); 370 | MODULE_AUTHOR("Nikita Shubin "); 371 | MODULE_DESCRIPTION("QEMU ivshmem virtual pci device"); 372 | -------------------------------------------------------------------------------- /vg_get_set.c: -------------------------------------------------------------------------------- 1 | /* 2 | * file: vg_get_set.c 3 | * desc: demo gpio manipulation utility 4 | * 5 | * Nikita Shubin, GPLv2 6 | * 7 | * derived from: 8 | * 9 | * file : ne_ivshmem_shm_guest_usr.c 10 | * desc : a demo program that updates/reads the ivshmem POSIX SHM region 11 | * 12 | * Siro Mugabi, Copyright (c) nairobi-embedded.org, GPLv2 13 | * 14 | * This program is free software; you can redistribute it and/or modify 15 | * it under the terms of the GNU General Public License as published by 16 | * the Free Software Foundation; either version 2 of the License, or 17 | * (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include "ivshmem-client.h" 39 | 40 | #include "virtual_gpio_basic.h" 41 | 42 | #define reg_size int32_t 43 | 44 | #ifndef BITS_PER_BYTE 45 | #define BITS_PER_BYTE 8 46 | #endif 47 | 48 | #ifndef DIV_ROUND_UP 49 | #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) 50 | #endif 51 | 52 | #ifndef BITS_TO_BYTES 53 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) 54 | #endif 55 | 56 | #define prfmt(fmt) "%s:%d:: " fmt, __func__, __LINE__ 57 | #define prinfo(fmt, ...) printf(prfmt(fmt), ##__VA_ARGS__) 58 | #define prerr(fmt, ...) fprintf(stderr, prfmt(fmt), ##__VA_ARGS__) 59 | 60 | #ifndef UNUSED 61 | #define UNUSED(x) (void)(x); 62 | #endif 63 | 64 | #ifndef EOK 65 | #define EOK 0 66 | #endif 67 | 68 | #ifndef IVSHMEM_FILE 69 | #define IVSHMEM_FILE "/dev/shm/ivshmem" 70 | #endif 71 | 72 | #define PACKAGE_NAME "vg_get_set" 73 | #define PACKAGE_VERSION "0.0.0" 74 | 75 | struct ivshmem_data { 76 | const char *filename; 77 | ssize_t filesize; 78 | enum { 79 | NE_IVSHMEM_READ, 80 | NE_IVSHMEM_WRITE, 81 | } ivshmem_op; 82 | }; 83 | 84 | #define IVSHMEM_CLIENT_DEFAULT_VERBOSE 1 85 | #define IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket" 86 | 87 | typedef struct IvshmemClientArgs { 88 | bool verbose; 89 | const char *unix_sock_path; 90 | } IvshmemClientArgs; 91 | 92 | static struct option long_options[] = 93 | { 94 | {"version", no_argument, 0, 'v'}, 95 | {"device", required_argument, 0, 'd'}, 96 | {"write-low", required_argument, 0, 'l'}, 97 | {"write-high", required_argument, 0, 'i'}, 98 | {"peer", required_argument, 0, 'p'}, 99 | {"ngpio", required_argument, 0, 'n'}, 100 | {"help", no_argument, 0, 'h'}, 101 | {0, 0, 0, 0} 102 | }; 103 | 104 | 105 | /************************************************************************** 106 | Function: Print Usage 107 | 108 | Description: 109 | Output the command-line options for this utility. 110 | 111 | Params: 112 | @argc - Standard argument count 113 | @argv - Standard argument array 114 | 115 | Returns: 116 | returns void always 117 | 118 | **************************************************************************/ 119 | void PrintUsage(int argc, char *argv[]) { 120 | UNUSED(argv); 121 | if(argc >=1 ) { 122 | printf( "-v, --version prints version and exits\n" \ 123 | "-d, --device=DEVICE specify device for usage e.g. [default="IVSHMEM_FILE"]\n" \ 124 | "-r, --read=NR reads corresponding gpio value, numeration starts from 0\n" \ 125 | "-l, --write-low=NR write low to corresponding gpio\n" \ 126 | "-i, --write-high=NR write high to corresponding gpio\n" \ 127 | "-p, --peer=ID notify peer_id of interrupt\n" \ 128 | "-n, --ngpio=CNT specify gpio number\n" \ 129 | "-h, --help prints this message\n"); 130 | } 131 | } 132 | 133 | /* listen on stdin (command line), on unix socket (notifications of new 134 | * and dead peers), and on eventfd (IRQ request) */ 135 | static int 136 | ivshmem_client_get_peer(IvshmemClient *client, int peer_id) 137 | { 138 | fd_set fds; 139 | int ret, maxfd = 0; 140 | 141 | 142 | do { 143 | FD_ZERO(&fds); 144 | maxfd = 0; 145 | ivshmem_client_get_fds(client, &fds, &maxfd); 146 | 147 | ret = select(maxfd, &fds, NULL, NULL, NULL); 148 | 149 | if (ret < 0) { 150 | if (errno == EINTR) { 151 | continue; 152 | } 153 | 154 | fprintf(stderr, "select error: %s\n", strerror(errno)); 155 | break; 156 | } 157 | if (ret == 0) { 158 | continue; 159 | } 160 | 161 | if (FD_ISSET(client->sock_fd, &fds) && ivshmem_client_handle_fds(client, &fds, maxfd) < 0) { 162 | fprintf(stderr, "ivshmem_client_handle_stdin_command() failed\n"); 163 | break; 164 | } 165 | 166 | } while(ivshmem_client_search_peer(client, peer_id) == NULL); 167 | 168 | return ret; 169 | } 170 | 171 | /* callback when we receive a notification (just display it) */ 172 | static void ivshmem_client_notification_cb(const IvshmemClient *client, 173 | const IvshmemClientPeer *peer, 174 | unsigned vect, void *arg) 175 | { 176 | (void)client; 177 | (void)arg; 178 | 179 | printf("receive notification from peer_id=%d vector=%u\n", peer->id, vect); 180 | } 181 | 182 | static unsigned int ngpio = VIRTUAL_GPIO_NR_GPIOS; 183 | 184 | /************************************************************************** 185 | Function: main 186 | 187 | Description: 188 | The c standard 'main' entry point function. 189 | 190 | Params: 191 | @argc - count of command line arguments given on command line 192 | @argv - array of arguments given on command line 193 | 194 | Returns: 195 | returns integer which is passed back to the parent process 196 | **************************************************************************/ 197 | int main(int argc, char **argv) 198 | { 199 | int ret = 0; 200 | 201 | int fd; 202 | void *map = NULL; 203 | ssize_t filesize = 0; 204 | struct ivshmem_data ivd; 205 | unsigned nr; 206 | unsigned val; 207 | const char *filename = NULL; 208 | 209 | int peer_id, vector = 0; 210 | 211 | ivd.filename = "/dev/shm/ivshmem"; /* default '/dev' node */ 212 | ivd.filesize = 0x100000; /* default mmio region size */ 213 | ivd.ivshmem_op = NE_IVSHMEM_READ; /* default op */ 214 | 215 | IvshmemClient client; 216 | IvshmemClientArgs args = { 217 | .verbose = IVSHMEM_CLIENT_DEFAULT_VERBOSE, 218 | .unix_sock_path = IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH, 219 | }; 220 | 221 | int c = -1; 222 | int option_index = 0; 223 | 224 | while((c = getopt_long(argc, argv, "vhd:l:i:r:p:n:", long_options, &option_index)) != -1) { 225 | switch(c){ 226 | case 'v' : 227 | { 228 | printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION); 229 | exit(EXIT_SUCCESS); 230 | break; 231 | } 232 | case 'd' : 233 | { 234 | if(access(optarg, F_OK) != EOK) 235 | { 236 | perror("Please give me correct --device option.\n"); 237 | exit(EXIT_FAILURE); 238 | } 239 | 240 | ivd.filename = optarg; 241 | break; 242 | } 243 | case 'l' : 244 | case 'i' : 245 | val = 0; 246 | if(c == 'i') 247 | val = 1; 248 | nr = strtol(optarg, 0, 0); 249 | ivd.ivshmem_op = NE_IVSHMEM_WRITE; 250 | break; 251 | case 'r' : 252 | { 253 | nr = strtol(optarg, 0, 0); 254 | ivd.ivshmem_op = NE_IVSHMEM_READ; 255 | break; 256 | } 257 | case 'p': 258 | peer_id = strtol(optarg, 0, 10); 259 | break; 260 | case 'h': 261 | PrintUsage(argc, argv); 262 | exit(EXIT_SUCCESS); 263 | break; 264 | case 'n': 265 | ngpio = strtol(optarg, 0, 10); 266 | default: 267 | break; 268 | } 269 | } 270 | 271 | if(nr > ngpio) 272 | { 273 | printf("Ping number is out of range. Number of gpios: %d\n", VIRTUAL_GPIO_NR_GPIOS); 274 | return EINVAL; 275 | } 276 | 277 | unsigned int nbytes = BITS_TO_BYTES(ngpio); 278 | 279 | filename = ivd.filename; 280 | filesize = ivd.filesize; 281 | 282 | if (ivshmem_client_init(&client, args.unix_sock_path, 283 | ivshmem_client_notification_cb, 284 | NULL, 285 | args.verbose) < 0) { 286 | fprintf(stderr, "cannot init client\n"); 287 | return 1; 288 | } 289 | 290 | if (ivshmem_client_connect(&client) < 0) { 291 | fprintf(stderr, "ivshmem_client: cannot connect to server.d\n"); 292 | exit(EXIT_FAILURE); 293 | } 294 | 295 | IvshmemClientPeer *peer = 0; 296 | 297 | ivshmem_client_get_peer(&client, peer_id); 298 | peer = ivshmem_client_search_peer(&client, peer_id); 299 | 300 | #ifdef DEBUG 301 | ivshmem_client_dump(&client); 302 | #endif 303 | 304 | if (peer == NULL) { 305 | printf("cannot find peer_id = %d\n", peer_id); 306 | exit(EXIT_FAILURE); 307 | } 308 | 309 | if ((fd = open(filename, O_RDWR)) < 0) 310 | { 311 | prerr("%s\n", strerror(errno)); 312 | exit(EXIT_FAILURE); 313 | } 314 | 315 | if ((map = mmap(0, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 316 | 0)) == (caddr_t)-1) 317 | { 318 | fprintf(stderr, "%s\n", strerror(errno)); 319 | close(fd); 320 | exit(EXIT_FAILURE); 321 | } 322 | 323 | reg_size *data, *output, *i_en, *i_st, *r_edge, *f_edge, *eoi; 324 | 325 | reg_size mask = (1 << nr); 326 | 327 | switch(ivd.ivshmem_op) 328 | { 329 | case NE_IVSHMEM_READ: 330 | data = map; 331 | val = !!(*data & (1 << nr)); 332 | prinfo("read %d=%d\n", nr, val); 333 | break; 334 | 335 | case NE_IVSHMEM_WRITE: 336 | data = map; 337 | output = map + VIRTUAL_GPIO_OUT_EN; 338 | i_en = map + VIRTUAL_GPIO_INT_EN; 339 | i_st = map + VIRTUAL_GPIO_INT_ST; 340 | r_edge = map + VIRTUAL_GPIO_RISING; 341 | f_edge = map + VIRTUAL_GPIO_FALLING; 342 | eoi = map + VIRTUAL_GPIO_INT_EOI; 343 | 344 | if(!!(*output & (1 << nr)) == 1) { // rework shift to mask 345 | prinfo("pin configurated as output not writing %d=%d\n", nr, val); 346 | break; 347 | } 348 | 349 | if (val) 350 | *data |= (1 << nr); 351 | else 352 | *data &= ~(1 << nr); 353 | 354 | prinfo("write %d=%d\n", nr, val); 355 | 356 | if(!!(*i_en & (1 << nr)) == 1) { 357 | prinfo("interrupt was enabled for %d\n", nr); 358 | 359 | /* check if interrupt already raised */ 360 | if(!!(*i_st & (1 << nr)) == 0) { 361 | prinfo("interrupt was raised for %d r_edge=0x%x f_edge=0x%x\n", nr, *r_edge, *f_edge); 362 | 363 | if(val && (!!(*r_edge & (1 << nr)) == 1)) 364 | { 365 | prinfo("rising edge interrupt was enabled for %d and new value is high %d\n", nr, val); 366 | 367 | /* set interrupt bit and raise interrupt */ 368 | *i_st |= (1 << nr); 369 | ivshmem_client_notify(&client, peer, vector); 370 | /* wait for eoi => move to sem */ 371 | /* eoi fast check and clear interrupt */ 372 | /* add counter check */ 373 | do {} while(!!(*eoi & (1 << nr)) != 1); 374 | *i_st &= ~(1 << nr); 375 | 376 | /* eoi should be always zero */ 377 | *eoi &= ~(1 << nr); 378 | } 379 | 380 | if(!val && (!!(*f_edge & (1 << nr)) == 1)) 381 | { 382 | prinfo("falling edge interrupt was enabled for %d and new value is low %d\n", nr, val); 383 | 384 | /* set interrupt bit and raise interrupt */ 385 | *i_st |= (1 << nr); 386 | ivshmem_client_notify(&client, peer, vector); 387 | /* wait for eoi => move to sem */ 388 | /* eoi fast check and clear interrupt */ 389 | /* add counter check */ 390 | do {} while(!!(*eoi & (1 << nr)) != 1); 391 | *i_st &= ~(1 << nr); 392 | 393 | /* eoi should be always zero */ 394 | *eoi &= ~(1 << nr); 395 | } 396 | } 397 | } 398 | break; 399 | default: 400 | prinfo("no read/write operations performed\n"); 401 | } 402 | 403 | if ((munmap(map, filesize)) < 0) 404 | prerr("WARNING: Failed to munmap \"%s\"\n", filename); 405 | 406 | peer_failure: 407 | fd_failure: 408 | close(fd); 409 | ivshmem_client_close(&client); 410 | 411 | return ret; 412 | } 413 | -------------------------------------------------------------------------------- /ivshmem-client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 6WIND S.A., 2014 3 | * 4 | * This work is licensed under the terms of the GNU GPL, version 2 or 5 | * (at your option) any later version. See the COPYING file in the 6 | * top-level directory. 7 | */ 8 | 9 | #include "qemu/osdep.h" 10 | #include 11 | #include 12 | 13 | #include "qemu/queue.h" 14 | 15 | #include "ivshmem-client.h" 16 | 17 | /* log a message on stdout if verbose=1 */ 18 | #define IVSHMEM_CLIENT_DEBUG(client, fmt, ...) do { \ 19 | if ((client)->verbose) { \ 20 | printf(fmt, ## __VA_ARGS__); \ 21 | } \ 22 | } while (0) 23 | 24 | /* read message from the unix socket */ 25 | static int 26 | ivshmem_client_read_one_msg(IvshmemClient *client, int64_t *index, int *fd) 27 | { 28 | int ret; 29 | struct msghdr msg; 30 | struct iovec iov[1]; 31 | union { 32 | struct cmsghdr cmsg; 33 | char control[CMSG_SPACE(sizeof(int))]; 34 | } msg_control; 35 | struct cmsghdr *cmsg; 36 | 37 | iov[0].iov_base = index; 38 | iov[0].iov_len = sizeof(*index); 39 | 40 | memset(&msg, 0, sizeof(msg)); 41 | msg.msg_iov = iov; 42 | msg.msg_iovlen = 1; 43 | msg.msg_control = &msg_control; 44 | msg.msg_controllen = sizeof(msg_control); 45 | 46 | ret = recvmsg(client->sock_fd, &msg, 0); 47 | if (ret < sizeof(*index)) { 48 | IVSHMEM_CLIENT_DEBUG(client, "cannot read message: %s\n", 49 | strerror(errno)); 50 | return -1; 51 | } 52 | if (ret == 0) { 53 | IVSHMEM_CLIENT_DEBUG(client, "lost connection to server\n"); 54 | return -1; 55 | } 56 | 57 | *index = GINT64_FROM_LE(*index); 58 | *fd = -1; 59 | 60 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 61 | 62 | if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || 63 | cmsg->cmsg_level != SOL_SOCKET || 64 | cmsg->cmsg_type != SCM_RIGHTS) { 65 | continue; 66 | } 67 | 68 | memcpy(fd, CMSG_DATA(cmsg), sizeof(*fd)); 69 | } 70 | 71 | return 0; 72 | } 73 | 74 | /* free a peer when the server advertises a disconnection or when the 75 | * client is freed */ 76 | static void 77 | ivshmem_client_free_peer(IvshmemClient *client, IvshmemClientPeer *peer) 78 | { 79 | unsigned vector; 80 | 81 | QTAILQ_REMOVE(&client->peer_list, peer, next); 82 | for (vector = 0; vector < peer->vectors_count; vector++) { 83 | close(peer->vectors[vector]); 84 | } 85 | 86 | g_free(peer); 87 | } 88 | 89 | /* handle message coming from server (new peer, new vectors) */ 90 | static int 91 | ivshmem_client_handle_server_msg(IvshmemClient *client) 92 | { 93 | IvshmemClientPeer *peer; 94 | int64_t peer_id; 95 | int ret, fd; 96 | 97 | ret = ivshmem_client_read_one_msg(client, &peer_id, &fd); 98 | if (ret < 0) { 99 | return -1; 100 | } 101 | 102 | /* can return a peer or the local client */ 103 | peer = ivshmem_client_search_peer(client, peer_id); 104 | 105 | /* delete peer */ 106 | if (fd == -1) { 107 | 108 | if (peer == NULL || peer == &client->local) { 109 | IVSHMEM_CLIENT_DEBUG(client, "receive delete for invalid " 110 | "peer %" PRId64 "\n", peer_id); 111 | return -1; 112 | } 113 | 114 | IVSHMEM_CLIENT_DEBUG(client, "delete peer id = %" PRId64 "\n", peer_id); 115 | ivshmem_client_free_peer(client, peer); 116 | return 0; 117 | } 118 | 119 | /* new peer */ 120 | if (peer == NULL) { 121 | peer = g_malloc0(sizeof(*peer)); 122 | peer->id = peer_id; 123 | peer->vectors_count = 0; 124 | QTAILQ_INSERT_TAIL(&client->peer_list, peer, next); 125 | IVSHMEM_CLIENT_DEBUG(client, "new peer id = %" PRId64 "\n", peer_id); 126 | } 127 | 128 | /* new vector */ 129 | IVSHMEM_CLIENT_DEBUG(client, " new vector %d (fd=%d) for peer id %" 130 | PRId64 "\n", peer->vectors_count, fd, peer->id); 131 | if (peer->vectors_count >= G_N_ELEMENTS(peer->vectors)) { 132 | IVSHMEM_CLIENT_DEBUG(client, "Too many vectors received, failing"); 133 | return -1; 134 | } 135 | 136 | peer->vectors[peer->vectors_count] = fd; 137 | peer->vectors_count++; 138 | 139 | return 0; 140 | } 141 | 142 | /* init a new ivshmem client */ 143 | int 144 | ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path, 145 | IvshmemClientNotifCb notif_cb, void *notif_arg, 146 | bool verbose) 147 | { 148 | int ret; 149 | unsigned i; 150 | 151 | memset(client, 0, sizeof(*client)); 152 | 153 | ret = snprintf(client->unix_sock_path, sizeof(client->unix_sock_path), 154 | "%s", unix_sock_path); 155 | 156 | if (ret < 0 || ret >= sizeof(client->unix_sock_path)) { 157 | IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n"); 158 | return -1; 159 | } 160 | 161 | for (i = 0; i < IVSHMEM_CLIENT_MAX_VECTORS; i++) { 162 | client->local.vectors[i] = -1; 163 | } 164 | 165 | QTAILQ_INIT(&client->peer_list); 166 | client->local.id = -1; 167 | 168 | client->notif_cb = notif_cb; 169 | client->notif_arg = notif_arg; 170 | client->verbose = verbose; 171 | client->shm_fd = -1; 172 | client->sock_fd = -1; 173 | 174 | return 0; 175 | } 176 | 177 | /* create and connect to the unix socket */ 178 | int 179 | ivshmem_client_connect(IvshmemClient *client) 180 | { 181 | struct sockaddr_un s_un; 182 | int fd, ret; 183 | int64_t tmp; 184 | 185 | IVSHMEM_CLIENT_DEBUG(client, "connect to client %s\n", 186 | client->unix_sock_path); 187 | 188 | client->sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); 189 | if (client->sock_fd < 0) { 190 | IVSHMEM_CLIENT_DEBUG(client, "cannot create socket: %s\n", 191 | strerror(errno)); 192 | return -1; 193 | } 194 | 195 | s_un.sun_family = AF_UNIX; 196 | ret = snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", 197 | client->unix_sock_path); 198 | if (ret < 0 || ret >= sizeof(s_un.sun_path)) { 199 | IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n"); 200 | goto err_close; 201 | } 202 | 203 | if (connect(client->sock_fd, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { 204 | IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", s_un.sun_path, 205 | strerror(errno)); 206 | goto err_close; 207 | } 208 | 209 | /* first, we expect a protocol version */ 210 | if (ivshmem_client_read_one_msg(client, &tmp, &fd) < 0 || 211 | (tmp != IVSHMEM_PROTOCOL_VERSION) || fd != -1) { 212 | IVSHMEM_CLIENT_DEBUG(client, "cannot read from server\n"); 213 | goto err_close; 214 | } 215 | 216 | /* then, we expect our index + a fd == -1 */ 217 | if (ivshmem_client_read_one_msg(client, &client->local.id, &fd) < 0 || 218 | client->local.id < 0 || fd != -1) { 219 | IVSHMEM_CLIENT_DEBUG(client, "cannot read from server (2)\n"); 220 | goto err_close; 221 | } 222 | IVSHMEM_CLIENT_DEBUG(client, "our_id=%" PRId64 "\n", client->local.id); 223 | 224 | /* now, we expect shared mem fd + a -1 index, note that shm fd 225 | * is not used */ 226 | if (ivshmem_client_read_one_msg(client, &tmp, &fd) < 0 || 227 | tmp != -1 || fd < 0) { 228 | if (fd >= 0) { 229 | close(fd); 230 | } 231 | IVSHMEM_CLIENT_DEBUG(client, "cannot read from server (3)\n"); 232 | goto err_close; 233 | } 234 | client->shm_fd = fd; 235 | IVSHMEM_CLIENT_DEBUG(client, "shm_fd=%d\n", fd); 236 | 237 | return 0; 238 | 239 | err_close: 240 | close(client->sock_fd); 241 | client->sock_fd = -1; 242 | return -1; 243 | } 244 | 245 | /* close connection to the server, and free all peer structures */ 246 | void 247 | ivshmem_client_close(IvshmemClient *client) 248 | { 249 | IvshmemClientPeer *peer; 250 | unsigned i; 251 | 252 | IVSHMEM_CLIENT_DEBUG(client, "close client\n"); 253 | 254 | while ((peer = QTAILQ_FIRST(&client->peer_list)) != NULL) { 255 | ivshmem_client_free_peer(client, peer); 256 | } 257 | 258 | close(client->shm_fd); 259 | client->shm_fd = -1; 260 | close(client->sock_fd); 261 | client->sock_fd = -1; 262 | client->local.id = -1; 263 | for (i = 0; i < IVSHMEM_CLIENT_MAX_VECTORS; i++) { 264 | close(client->local.vectors[i]); 265 | client->local.vectors[i] = -1; 266 | } 267 | client->local.vectors_count = 0; 268 | } 269 | 270 | /* get the fd_set according to the unix socket and peer list */ 271 | void 272 | ivshmem_client_get_fds(const IvshmemClient *client, fd_set *fds, int *maxfd) 273 | { 274 | int fd; 275 | unsigned vector; 276 | 277 | FD_SET(client->sock_fd, fds); 278 | if (client->sock_fd >= *maxfd) { 279 | *maxfd = client->sock_fd + 1; 280 | } 281 | 282 | for (vector = 0; vector < client->local.vectors_count; vector++) { 283 | fd = client->local.vectors[vector]; 284 | FD_SET(fd, fds); 285 | if (fd >= *maxfd) { 286 | *maxfd = fd + 1; 287 | } 288 | } 289 | } 290 | 291 | /* handle events from eventfd: just print a message on notification */ 292 | static int 293 | ivshmem_client_handle_event(IvshmemClient *client, const fd_set *cur, int maxfd) 294 | { 295 | IvshmemClientPeer *peer; 296 | uint64_t kick; 297 | unsigned i; 298 | int ret; 299 | 300 | peer = &client->local; 301 | 302 | for (i = 0; i < peer->vectors_count; i++) { 303 | if (peer->vectors[i] >= maxfd || !FD_ISSET(peer->vectors[i], cur)) { 304 | continue; 305 | } 306 | 307 | ret = read(peer->vectors[i], &kick, sizeof(kick)); 308 | if (ret < 0) { 309 | return ret; 310 | } 311 | if (ret != sizeof(kick)) { 312 | IVSHMEM_CLIENT_DEBUG(client, "invalid read size = %d\n", ret); 313 | errno = EINVAL; 314 | return -1; 315 | } 316 | IVSHMEM_CLIENT_DEBUG(client, "received event on fd %d vector %d: %" 317 | PRIu64 "\n", peer->vectors[i], i, kick); 318 | if (client->notif_cb != NULL) { 319 | client->notif_cb(client, peer, i, client->notif_arg); 320 | } 321 | } 322 | 323 | return 0; 324 | } 325 | 326 | /* read and handle new messages on the given fd_set */ 327 | int 328 | ivshmem_client_handle_fds(IvshmemClient *client, fd_set *fds, int maxfd) 329 | { 330 | if (client->sock_fd < maxfd && FD_ISSET(client->sock_fd, fds) && 331 | ivshmem_client_handle_server_msg(client) < 0 && errno != EINTR) { 332 | IVSHMEM_CLIENT_DEBUG(client, "ivshmem_client_handle_server_msg() " 333 | "failed\n"); 334 | return -1; 335 | } else if (ivshmem_client_handle_event(client, fds, maxfd) < 0 && 336 | errno != EINTR) { 337 | IVSHMEM_CLIENT_DEBUG(client, "ivshmem_client_handle_event() failed\n"); 338 | return -1; 339 | } 340 | 341 | return 0; 342 | } 343 | 344 | /* send a notification on a vector of a peer */ 345 | int 346 | ivshmem_client_notify(const IvshmemClient *client, 347 | const IvshmemClientPeer *peer, unsigned vector) 348 | { 349 | uint64_t kick; 350 | int fd; 351 | 352 | if (vector >= peer->vectors_count) { 353 | IVSHMEM_CLIENT_DEBUG(client, "invalid vector %u on peer %" PRId64 "\n", 354 | vector, peer->id); 355 | return -1; 356 | } 357 | fd = peer->vectors[vector]; 358 | IVSHMEM_CLIENT_DEBUG(client, "notify peer %" PRId64 359 | " on vector %d, fd %d\n", peer->id, vector, fd); 360 | 361 | kick = 1; 362 | if (write(fd, &kick, sizeof(kick)) != sizeof(kick)) { 363 | fprintf(stderr, "could not write to %d: %s\n", peer->vectors[vector], 364 | strerror(errno)); 365 | return -1; 366 | } 367 | return 0; 368 | } 369 | 370 | /* send a notification to all vectors of a peer */ 371 | int 372 | ivshmem_client_notify_all_vects(const IvshmemClient *client, 373 | const IvshmemClientPeer *peer) 374 | { 375 | unsigned vector; 376 | int ret = 0; 377 | 378 | for (vector = 0; vector < peer->vectors_count; vector++) { 379 | if (ivshmem_client_notify(client, peer, vector) < 0) { 380 | ret = -1; 381 | } 382 | } 383 | 384 | return ret; 385 | } 386 | 387 | /* send a notification to all peers */ 388 | int 389 | ivshmem_client_notify_broadcast(const IvshmemClient *client) 390 | { 391 | IvshmemClientPeer *peer; 392 | int ret = 0; 393 | 394 | QTAILQ_FOREACH(peer, &client->peer_list, next) { 395 | if (ivshmem_client_notify_all_vects(client, peer) < 0) { 396 | ret = -1; 397 | } 398 | } 399 | 400 | return ret; 401 | } 402 | 403 | /* lookup peer from its id */ 404 | IvshmemClientPeer * 405 | ivshmem_client_search_peer(IvshmemClient *client, int64_t peer_id) 406 | { 407 | IvshmemClientPeer *peer; 408 | 409 | if (peer_id == client->local.id) { 410 | return &client->local; 411 | } 412 | 413 | QTAILQ_FOREACH(peer, &client->peer_list, next) { 414 | if (peer->id == peer_id) { 415 | return peer; 416 | } 417 | } 418 | return NULL; 419 | } 420 | 421 | /* dump our info, the list of peers their vectors on stdout */ 422 | void 423 | ivshmem_client_dump(const IvshmemClient *client) 424 | { 425 | const IvshmemClientPeer *peer; 426 | unsigned vector; 427 | 428 | /* dump local infos */ 429 | peer = &client->local; 430 | printf("our_id = %" PRId64 "\n", peer->id); 431 | for (vector = 0; vector < peer->vectors_count; vector++) { 432 | printf(" vector %d is enabled (fd=%d)\n", vector, 433 | peer->vectors[vector]); 434 | } 435 | 436 | /* dump peers */ 437 | QTAILQ_FOREACH(peer, &client->peer_list, next) { 438 | printf("peer_id = %" PRId64 "\n", peer->id); 439 | 440 | for (vector = 0; vector < peer->vectors_count; vector++) { 441 | printf(" vector %d is enabled (fd=%d)\n", vector, 442 | peer->vectors[vector]); 443 | } 444 | } 445 | } 446 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------