From: Alon Levy <alevy@redhat.com>
To: Anthony Liguori <anthony@codemonkey.ws>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 06/20] usb-ccid: review fixes (v15-v16)
Date: Thu, 3 Feb 2011 20:53:39 +0200 [thread overview]
Message-ID: <20110203185338.GN23953@playa.tlv.redhat.com> (raw)
In-Reply-To: <4D4ADC43.90709@codemonkey.ws>
On Thu, Feb 03, 2011 at 10:48:03AM -0600, Anthony Liguori wrote:
> On 02/02/2011 02:28 PM, Alon Levy wrote:
> >I'll fold it before submitting the version to be applied, but
> >I hope keeping it as a separate patch will make reviewing easier.
>
> Hrm, can you just send out the new patches? It's actually harder
> to review like this.
Yes.
>
> Regards,
>
> Anthony Liguori
>
> >Behavioral changes:
> > * fix abort on client answer after card remove
> > * enable migration
> > * remove side affect code from asserts
> > * return consistent self-powered state
> > * mask out reserved bits in ccid_set_parameters
> > * add missing abRFU in SetParameters (no affect on linux guest)
> >
> >whitefixes / comments / consts defines:
> > * remove stale comment
> > * remove ccid_print_pending_answers if no DEBUG_CCID
> > * replace printf's with DPRINTF, remove DEBUG_CCID, add verbosity defines
> > * use error_report
> > * update copyright (most of the code is not original)
> > * reword known bug comment
> > * add missing closing quote in comment
> > * add missing whitespace on one line
> > * s/CCID_SetParameter/CCID_SetParameters/
> > * add comments
> > * use define for max packet size
> >
> >Comment for "return consistent self-powered state":
> >
> >the Configuration Descriptor bmAttributes claims we are self powered,
> >but we were returning not self powered to USB_REQ_GET_STATUS control message.
> >
> >In practice, this message is not sent by a linux 2.6.35.10-74.fc14.x86_64
> >guest (not tested on other guests), unless you issue lsusb -v as root (for
> >example).
> >
> >Signed-off-by: Alon Levy<alevy@redhat.com>
> >---
> > hw/ccid.h | 7 +++
> > hw/usb-ccid.c | 115 ++++++++++++++++++++++++++++-----------------------------
> > 2 files changed, 63 insertions(+), 59 deletions(-)
> >
> >diff --git a/hw/ccid.h b/hw/ccid.h
> >index af59070..f5dcfae 100644
> >--- a/hw/ccid.h
> >+++ b/hw/ccid.h
> >@@ -6,11 +6,16 @@
> > typedef struct CCIDCardState CCIDCardState;
> > typedef struct CCIDCardInfo CCIDCardInfo;
> >
> >+/* state of the CCID Card device (i.e. hw/ccid-card-*.c)
> >+ */
> > struct CCIDCardState {
> > DeviceState qdev;
> > uint32_t slot; // For future use with multiple slot reader.
> > };
> >
> >+/* callbacks to be used by the CCID device (hw/usb-ccid.c) to call
> >+ * into the smartcard device (hw/ccid-card-*.c)
> >+ */
> > struct CCIDCardInfo {
> > DeviceInfo qdev;
> > void (*print)(Monitor *mon, CCIDCardState *card, int indent);
> >@@ -20,6 +25,8 @@ struct CCIDCardInfo {
> > int (*initfn)(CCIDCardState *card);
> > };
> >
> >+/* API for smartcard calling the CCID device (used by hw/ccid-card-*.c)
> >+ */
> > void ccid_card_send_apdu_to_guest(CCIDCardState *card, uint8_t* apdu, uint32_t len);
> > void ccid_card_card_removed(CCIDCardState *card);
> > void ccid_card_card_inserted(CCIDCardState *card);
> >diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
> >index 58f69a6..09e39ac 100644
> >--- a/hw/usb-ccid.c
> >+++ b/hw/usb-ccid.c
> >@@ -1,15 +1,18 @@
> > /*
> > * CCID Device emulation
> > *
> >- * Based on usb-serial.c:
> >+ * Written by Alon Levy, with contributions from Robert Relyea.
> >+ *
> >+ * Based on usb-serial.c, see it's copyright and attributions below.
> >+ *
> >+ * This code is licenced under the GNU LGPL, version 2 or later.
> >+ *
> >+ * -------
> >+ *
> >+ * usb-serial.c copyright and attribution:
> > * Copyright (c) 2006 CodeSourcery.
> > * Copyright (c) 2008 Samuel Thibault<samuel.thibault@ens-lyon.org>
> > * Written by Paul Brook, reused for FTDI by Samuel Thibault,
> >- * Reused for CCID by Alon Levy.
> >- * Contributed to by Robert Relyea
> >- * Copyright (c) 2010 Red Hat.
> >- *
> >- * This code is licenced under the LGPL.
> > */
> >
> > /* References:
> >@@ -19,22 +22,16 @@
> > * Specification for Integrated Circuit(s) Cards Interface Devices
> > *
> > * Endianess note: from the spec (1.3)
> >- * "Fields that are larger than a byte are stored in little endian
> >+ * "Fields that are larger than a byte are stored in little endian"
> > *
> > * KNOWN BUGS
> > * 1. remove/insert can sometimes result in removed state instead of inserted.
> > * This is a result of the following:
> >- * symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This happens
> >- * when we send a too short packet, seen in uhci-usb.c, resulting from
> >- * a urb requesting SPD and us returning a smaller packet.
> >+ * symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This can happen
> >+ * when a short packet is sent, as seen in uhci-usb.c, resulting from a urb
> >+ * from the guest requesting SPD and us returning a smaller packet.
> > * Not sure which messages trigger this.
> > *
> >- * Migration note:
> >- *
> >- * All the VMStateDescription's are left here for future use, but
> >- * not enabled right now since there is no support for USB migration.
> >- *
> >- * To enable define ENABLE_MIGRATION
> > */
> >
> > #include "qemu-common.h"
> >@@ -44,11 +41,14 @@
> >
> > #include "hw/ccid.h"
> >
> >-//#define DEBUG_CCID
> >-
> > #define DPRINTF(s, lvl, fmt, ...) \
> > do { if (lvl<= s->debug) { printf("usb-ccid: " fmt , ## __VA_ARGS__); } } while (0)
> >
> >+#define D_WARN 1
> >+#define D_INFO 2
> >+#define D_MORE_INFO 3
> >+#define D_VERBOSE 4
> >+
> > #define CCID_DEV_NAME "usb-ccid"
> >
> > /* The two options for variable sized buffers:
> >@@ -64,6 +64,8 @@ do { if (lvl<= s->debug) { printf("usb-ccid: " fmt , ## __VA_ARGS__); } } while
> > #define InterfaceOutClass ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
> > #define InterfaceInClass ((USB_DIR_IN |USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
> >
> >+#define CCID_MAX_PACKET_SIZE 64
> >+
> > #define CCID_CONTROL_ABORT 0x1
> > #define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x2
> > #define CCID_CONTROL_GET_DATA_RATES 0x3
> >@@ -209,11 +211,12 @@ typedef struct __attribute__ ((__packed__)) CCID_IccPowerOff {
> > uint16_t abRFU;
> > } CCID_IccPowerOff;
> >
> >-typedef struct __attribute__ ((__packed__)) CCID_SetParameter {
> >+typedef struct __attribute__ ((__packed__)) CCID_SetParameters {
> > CCID_Header hdr;
> > uint8_t bProtocolNum;
> >+ uint16_t abRFU;
> > uint8_t abProtocolDataStructure[0];
> >-} CCID_SetParameter;
> >+} CCID_SetParameters;
> >
> > typedef struct CCID_Notify_Slot_Change {
> > uint8_t bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */
> >@@ -278,10 +281,6 @@ struct USBCCIDState {
> > uint16_t migration_target_port;
> > };
> >
> >-/* Slot specific variables. We emulate a single slot card reader.
> >- */
> >-
> >-
> > /* CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9,
> > * "USB Device Framework", section 9.6.1, in the Universal Serial Bus
> > * Specification.
> >@@ -416,7 +415,8 @@ static const uint8_t qemu_ccid_config_descriptor[] = {
> > /* u8 ep_bEndpointAddress; IN Endpoint 1 */
> > 0x80 | CCID_INT_IN_EP,
> > 0x03, /* u8 ep_bmAttributes; Interrupt */
> >- 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
> >+ /* u16 ep_wMaxPacketSize; */
> >+ CCID_MAX_PACKET_SIZE& 0xff, (CCID_MAX_PACKET_SIZE>> 8),
> > 0xff, /* u8 ep_bInterval; */
> >
> > /* Bulk-In endpoint */
> >@@ -455,32 +455,31 @@ static void ccid_clear_pending_answers(USBCCIDState *s)
> >
> > static void ccid_print_pending_answers(USBCCIDState *s)
> > {
> >-#ifdef DEBUG_CCID
> > Answer *answer;
> > int i, count;
> >
> >- printf("usb-ccid: pending answers:");
> >+ DPRINTF(s, D_VERBOSE, "usb-ccid: pending answers:");
> > if (!ccid_has_pending_answers(s)) {
> >- printf(" empty\n");
> >+ DPRINTF(s, D_VERBOSE, " empty\n");
> > return;
> > }
> > for (i = s->pending_answers_start, count=s->pending_answers_num ;
> > count> 0; count--, i++) {
> > answer =&s->pending_answers[i % PENDING_ANSWERS_NUM];
> > if (count == 1) {
> >- printf("%d:%d\n", answer->slot, answer->seq);
> >+ DPRINTF(s, D_VERBOSE, "%d:%d\n", answer->slot, answer->seq);
> > } else {
> >- printf("%d:%d,", answer->slot, answer->seq);
> >+ DPRINTF(s, D_VERBOSE, "%d:%d,", answer->slot, answer->seq);
> > }
> > }
> >-#endif
> > }
> >
> > static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr)
> > {
> > Answer* answer;
> >
> >- assert(s->pending_answers_num++< PENDING_ANSWERS_NUM);
> >+ assert(s->pending_answers_num< PENDING_ANSWERS_NUM);
> >+ s->pending_answers_num++;
> > answer =&s->pending_answers[(s->pending_answers_end++) % PENDING_ANSWERS_NUM];
> > answer->slot = hdr->bSlot;
> > answer->seq = hdr->bSeq;
> >@@ -492,7 +491,8 @@ static void ccid_remove_pending_answer(USBCCIDState *s,
> > {
> > Answer *answer;
> >
> >- assert(s->pending_answers_num--> 0);
> >+ assert(s->pending_answers_num> 0);
> >+ s->pending_answers_num--;
> > answer =&s->pending_answers[(s->pending_answers_start++) % PENDING_ANSWERS_NUM];
> > *slot = answer->slot;
> > *seq = answer->seq;
> >@@ -528,16 +528,16 @@ static void* ccid_reserve_recv_buf(USBCCIDState* s, uint16_t len)
> > {
> > BulkIn* bulk_in;
> >
> >- DPRINTF(s, 4, "%s: QUEUE: reserve %d bytes\n", __func__, len);
> >+ DPRINTF(s, D_VERBOSE, "%s: QUEUE: reserve %d bytes\n", __func__, len);
> >
> > /* look for an existing element */
> > if (len> BULK_IN_BUF_SIZE) {
> >- printf("usb-ccid.c: %s: len larger then max (%d>%d). discarding message.\n",
> >+ DPRINTF(s, D_WARN, "usb-ccid.c: %s: len larger then max (%d>%d). discarding message.\n",
> > __func__, len, BULK_IN_BUF_SIZE);
> > return NULL;
> > }
> > if (s->bulk_in_pending_num>= BULK_IN_PENDING_NUM) {
> >- printf("usb-ccid.c: %s: No free bulk_in buffers. discarding message.\n",
> >+ DPRINTF(s, D_WARN, "usb-ccid.c: %s: No free bulk_in buffers. discarding message.\n",
> > __func__);
> > return NULL;
> > }
> >@@ -576,7 +576,7 @@ static int ccid_handle_control(USBDevice *dev, int request, int value,
> > DPRINTF(s, 1, "got control %x, value %x\n",request, value);
> > switch (request) {
> > case DeviceRequest | USB_REQ_GET_STATUS:
> >- data[0] = (0<< USB_DEVICE_SELF_POWERED) |
> >+ data[0] = (1<< USB_DEVICE_SELF_POWERED) |
> > (dev->remote_wakeup<< USB_DEVICE_REMOTE_WAKEUP);
> > data[1] = 0x00;
> > ret = 2;
> >@@ -709,7 +709,7 @@ static uint8_t ccid_calc_status(USBCCIDState *s)
> > bmCommandStatus
> > */
> > uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus<< 6);
> >- DPRINTF(s, 4, "status = %d\n", ret);
> >+ DPRINTF(s, D_VERBOSE, "status = %d\n", ret);
> > return ret;
> > }
> >
> >@@ -770,11 +770,9 @@ static void ccid_write_data_block(
> > p->b.hdr.bSeq = seq;
> > p->b.bStatus = ccid_calc_status(s);
> > p->b.bError = s->bError;
> >-#ifdef DEBUG_CCID
> > if (p->b.bError) {
> >- DPRINTF(s, 4, "error %d", p->b.bError);
> >+ DPRINTF(s, D_VERBOSE, "error %d", p->b.bError);
> > }
> >-#endif
> > memcpy(p->abData, data, len);
> > ccid_reset_error_status(s);
> > }
> >@@ -805,12 +803,12 @@ static void ccid_write_data_block_atr(USBCCIDState* s, CCID_Header* recv)
> >
> > static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
> > {
> >- CCID_SetParameter *ph = (CCID_SetParameter *) recv;
> >+ CCID_SetParameters *ph = (CCID_SetParameters *) recv;
> > uint32_t len = 0;
> >- if (ph->bProtocolNum == 0) {
> >+ if ((ph->bProtocolNum& 3) == 0) {
> > len = 5;
> > }
> >- if (ph->bProtocolNum == 1) {
> >+ if ((ph->bProtocolNum& 3) == 1) {
> > len = 7;
> > }
> > if (len == 0) {
> >@@ -884,7 +882,7 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
> > if (s->card) {
> > s->cardinfo->apdu_from_guest(s->card, recv->abData, len);
> > } else {
> >- printf("warning: discarded apdu\n");
> >+ DPRINTF(s, D_WARN, "warning: discarded apdu\n");
> > }
> > }
> >
> >@@ -901,15 +899,15 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
> > ccid_header = (CCID_Header*)s->bulk_out_data;
> > memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len);
> > s->bulk_out_pos += p->len;
> >- if (p->len == 64) {
> >- DPRINTF(s, 4, "usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
> >+ if (p->len == CCID_MAX_PACKET_SIZE) {
> >+ DPRINTF(s, D_VERBOSE, "usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
> > p->len, ccid_header->dwLength);
> > return 0;
> > }
> > if (s->bulk_out_pos< 10) {
> > DPRINTF(s, 1, "%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n", __func__);
> > } else {
> >- DPRINTF(s, 3, "%s %x\n", __func__, ccid_header->bMessageType);
> >+ DPRINTF(s, D_MORE_INFO, "%s %x\n", __func__, ccid_header->bMessageType);
> > switch (ccid_header->bMessageType) {
> > case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
> > ccid_write_slot_status(s, ccid_header);
> >@@ -965,7 +963,7 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
> > {
> > int ret = 0;
> >
> >- assert(len>0);
> >+ assert(len> 0);
> > ccid_bulk_in_get(s);
> > if (s->current_bulk_in != NULL) {
> > ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len);
> >@@ -978,7 +976,7 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
> > ret = USB_RET_NAK; /* return when device has no data - usb 2.0 spec Table 8-4 */
> > }
> > if (ret> 0) {
> >- DPRINTF(s, 3, "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret);
> >+ DPRINTF(s, D_MORE_INFO, "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret);
> > }
> > if (ret != USB_RET_NAK&& ret< len) {
> > DPRINTF(s, 1, "%s: returning short (EREMOTEIO) %d< %d\n", __func__, ret, len);
> >@@ -1015,7 +1013,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
> > ret = 2;
> > s->notify_slot_change = false;
> > s->bmSlotICCState&= ~SLOT_0_CHANGED_MASK;
> >- DPRINTF(s, 2, "handle_data: int_in: notify_slot_change %X, requested len %d\n",
> >+ DPRINTF(s, D_INFO, "handle_data: int_in: notify_slot_change %X, requested len %d\n",
> > s->bmSlotICCState, len);
> > }
> > break;
> >@@ -1146,7 +1144,11 @@ void ccid_card_card_error(CCIDCardState *card, uint64_t error)
> > DPRINTF(s, 1, "VSC_Error: %lX\n", s->last_answer_error);
> > /* TODO: these error's should be more verbose and propogated to the guest.
> > * */
> >- ccid_write_data_block_answer(s, NULL, 0);
> >+ /* we flush all pending answers on CardRemove message in ccid-card-passthru,
> >+ * so check that first to not trigger abort */
> >+ if (ccid_has_pending_answers(s)) {
> >+ ccid_write_data_block_answer(s, NULL, 0);
> >+ }
> > }
> >
> > void ccid_card_card_inserted(CCIDCardState *card)
> >@@ -1184,12 +1186,12 @@ static int ccid_card_init(DeviceState *qdev, DeviceInfo *base)
> > int ret = 0;
> >
> > if (card->slot != 0) {
> >- fprintf(stderr, "Warning: usb-ccid supports one slot, can't add %d",
> >+ error_report("Warning: usb-ccid supports one slot, can't add %d",
> > card->slot);
> > return -1;
> > }
> > if (s->card != NULL) {
> >- fprintf(stderr, "Warning: usb-ccid card already full, not adding\n");
> >+ error_report("Warning: usb-ccid card already full, not adding\n");
> > return -1;
> > }
> > ret = info->initfn ? info->initfn(card) : ret;
> >@@ -1233,7 +1235,6 @@ static int ccid_initfn(USBDevice *dev)
> > return 0;
> > }
> >
> >-#ifdef ENABLE_MIGRATION
> > static int ccid_post_load(void *opaque, int version_id)
> > {
> > USBCCIDState *s = opaque;
> >@@ -1325,7 +1326,6 @@ static VMStateDescription ccid_vmstate = {
> > VMSTATE_END_OF_LIST()
> > }
> > };
> >-#endif // ENABLE_MIGRATION
> >
> > static struct USBDeviceInfo ccid_info = {
> > .product_desc = "QEMU USB CCID",
> >@@ -1342,12 +1342,9 @@ static struct USBDeviceInfo ccid_info = {
> > DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0),
> > DEFINE_PROP_END_OF_LIST(),
> > },
> >-#ifdef ENABLE_MIGRATION
> > .qdev.vmsd =&ccid_vmstate,
> >-#endif
> > };
> >
> >-
> > static void ccid_register_devices(void)
> > {
> > usb_qdev_register(&ccid_info);
>
next prev parent reply other threads:[~2011-02-03 18:54 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-02 20:28 [Qemu-devel] [PATCH 00/20] usb-ccid (v16) Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 01/20] qdev: add print_options callback Alon Levy
2011-02-03 16:39 ` Anthony Liguori
2011-02-03 20:54 ` Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 02/20] qdev: add data pointer to Property Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 03/20] qdev-properties: add PROP_TYPE_ENUM Alon Levy
2011-02-03 16:44 ` Anthony Liguori
2011-02-03 20:59 ` Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 04/20] qdev-properties: parse_enum: don't cast a void* Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 05/20] usb-ccid: add CCID bus Alon Levy
2011-02-03 16:46 ` Anthony Liguori
2011-02-03 18:53 ` Alon Levy
2011-02-03 19:29 ` Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 06/20] usb-ccid: review fixes (v15-v16) Alon Levy
2011-02-03 16:48 ` Anthony Liguori
2011-02-03 18:53 ` Alon Levy [this message]
2011-02-02 20:28 ` [Qemu-devel] [PATCH 07/20] introduce libcacard/vscard_common.h Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 08/20] libcacard/vscard_common.h update (v15->v16) Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 09/20] ccid: add passthru card device Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 10/20] ccid-card-passthru: review fixes (v15->v16) Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 11/20] libcacard: initial commit after coding style fixes Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 12/20] libcacard: fixes from review (v15->v16) Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 13/20] ccid: add ccid-card-emulated device (v2) Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 14/20] ccid-card-emulated: use PROP_TYPE_ENUM for backend Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 15/20] ccid-card-emulated: review fixes (v15->v16) Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 16/20] ccid: add docs Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 17/20] ccid: configure: add --enable/disable and nss only disable Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 18/20] ccid: add qdev description strings Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 19/20] smartcard, configure: add --enable-smartcard-nss, report only nss Alon Levy
2011-02-02 20:28 ` [Qemu-devel] [PATCH 20/20] smartcard,configure: " Alon Levy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20110203185338.GN23953@playa.tlv.redhat.com \
--to=alevy@redhat.com \
--cc=anthony@codemonkey.ws \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).