9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: "Fco. J. Ballesteros" <nemo@lsub.org>
To: 9fans@cse.psu.edu
Subject: Re: [9fans] usbd problem
Date: Tue, 12 Feb 2008 15:42:03 +0100	[thread overview]
Message-ID: <e9cf4f97d406fc40f6ab5c21d6103125@lsub.org> (raw)
In-Reply-To: <964c1529df94a5c6ba189fb0520afbc5@plan9.bell-labs.com>

Warning!, the dump.c I sent Sape has a bug.

search for "len = b[0]-1", in pdesc(), replace that with
"len = b[0]".

sorry for the mistake.


>  From: sape@plan9.bell-labs.com
>  To: 9fans@cse.psu.edu
>  Reply-To: 9fans@cse.psu.edu
>  Date: Tue Feb 12 15:36:36 CET 2008
>  Subject: Re: [9fans] usbd problem
>  
>  >> Nemo found a bug in usb/lib/dump.c that may well account for
>  >> this. I incorporated his change (and fixed a bug in his code :-).
>  >> I'll ask Geoff to push it out today.
>  >> 
>  >> 	Sape
>  > 
>  > excellent. i'll give it a shot.
>  > 
>  > - erik
>  
>  Give it a shot now:
>  	/sys/src/cmd/usb/lib/dump.c
>  
>  
>  !— dump.c.txt
>  
>  #include <u.h>
>  #include <libc.h>
>  #include <thread.h>
>  #include <bio.h>
>  #include "usb.h"
>  
>  int verbose;
>  
>  typedef struct Flags Flags;
>  typedef struct Classes Classes;
>  
>  struct Flags {
>  	int	bit;
>  	char*	name0;
>  	char*	name1;
>  };
>  
>  struct Classes {
>  	char*	name;
>  	struct {
>  		char*	name;
>  		char*	proto[4];
>  	} subclass[4];
>  };
>  
>  static Classes	classname[] = {
>  	[CL_AUDIO]	{"audio",	{[1]{"control"}, [2]{"stream"}, [3]{"midi"}}},
>  	[CL_COMMS]	{"comms",	{[1] {"abstract", {[1]"AT"}}}},
>  	[CL_HID]	{"hid",		{[1] {"boot", {[1]"kbd", [2]"mouse"}}}},
>  	[CL_PRINTER]	{"printer",	{[1]"printer", {[1]"uni", [2]"bi"}}},
>  	[CL_HUB]	{"hub",		{[1]{"hub"}}},
>  	[CL_DATA]	{"data"},
>  };
>  
>  static	void	pflag(Flags*, uint);
>  
>  char *
>  sclass(char *p, char *e, ulong csp)
>  {
>  	Classes *cs;
>  	int c, s, pr;
>  
>  	c = Class(csp);
>  	s = Subclass(csp);
>  	pr = Proto(csp);
>  	if(c < 0 || c >= nelem(classname) || (cs = &classname[c])->name == nil)
>  		return seprint(p, e, "%d.%d.%d", c, s, pr);
>  	p = seprint(p, e, "%s.", cs->name);
>  	if(s < 0 || s >= nelem(cs->subclass) || cs->subclass[s].name == nil)
>  		p = seprint(p, e, "%d.%d", s, pr);
>  	else{
>  		p = seprint(p, e, "%s.", cs->subclass[s].name);
>  		if(pr < 0 || pr >= nelem(cs->subclass[s].proto) || cs->subclass[s].proto[pr] == nil)
>  			p = seprint(p, e, "%d", pr);
>  		else
>  			p = seprint(p, e, "%s", cs->subclass[s].proto[pr]);
>  	}
>  	return p;
>  }
>  
>  void
>  pdevice(Device *, int, ulong, void *b, int n)
>  {
>  	DDevice *d;
>  	char scbuf[64];
>  
>  	if(n < DDEVLEN)
>  		return;
>  	d = b;
>  	if(debug & Dbginfo) {
>  		fprint(2, "usb (bcd)%c%c%c%c",
>  				'0'+((d->bcdUSB[1]>>4)&0xf), '0'+(d->bcdUSB[1]&0xf),
>  				'0'+((d->bcdUSB[0]>>4)&0xf), '0'+(d->bcdUSB[0]&0xf));
>  		sclass(scbuf, scbuf + sizeof scbuf,
>  			CSP(d->bDeviceClass, d->bDeviceSubClass, d->bDeviceProtocol)),
>  		fprint(2, " class %d subclass %d proto %d [%s] max0 %d",
>  			d->bDeviceClass, d->bDeviceSubClass, d->bDeviceProtocol,
>  			scbuf,
>  			d->bMaxPacketSize0);
>  		fprint(2, " vendor %#x product %#x device (bcd)%c%c%c%c",
>  			GET2(d->idVendor), GET2(d->idProduct),
>  			'0'+((d->bcdDevice[1]>>4)&0xf), '0'+(d->bcdDevice[1]&0xf),
>  			'0'+((d->bcdDevice[0]>>4)&0xf), '0'+(d->bcdDevice[0]&0xf));
>  		fprint(2, " man %d prod %d serial %d nconfig %d",
>  			d->iManufacturer, d->iProduct, d->iSerialNumber,
>  			d->bNumConfigurations);
>  	}
>  }
>  
>  void
>  phid(Device *, int, ulong, void *b, int n)
>  {
>  	DHid *d;
>  
>  	if(n < DHIDLEN){
>  		fprint(2, "%s: hid too short\n", argv0);
>  		return;
>  	}
>  	d = b;
>  	if(debug & Dbginfo)
>  		fprint(2, "HID (bcd)%c%c%c%c country %d nhidclass %d classdtype %#x dlen %d\n",
>  			'0'+((d->bcdHID[1]>>4)&0xf), '0'+(d->bcdHID[1]&0xf),
>  			'0'+((d->bcdHID[0]>>4)&0xf), '0'+(d->bcdHID[0]&0xf),
>  			d->bCountryCode, d->bNumDescriptors,
>  			d->bClassDescriptorType, GET2(d->wItemLength));
>  }
>  
>  static	Flags	ioflags[] = {
>  	{0, "Data", "Constant"},
>  	{1, "Array", "Variable"},
>  	{2, "Absolute", "Relative"},
>  	{3, "NoWrap", "Wrap"},
>  	{4, "Linear", "NonLinear"},
>  	{5, "PreferredState", "No Preferred State"},
>  	{6, "No Null position", "Null state"},
>  	{7, "Non Volatile", "Volatile"},
>  	{8, "Bit Field", "Buffered Bytes"},
>  	{-1, nil, nil},
>  };
>  
>  static void
>  pflag(Flags *tab, uint v)
>  {
>  	char buf[200], *s;
>  	int n;
>  
>  	n = 0;
>  	buf[0] = 0;
>  	for(; tab->name0 != nil; tab++){
>  		if(v & (1<<tab->bit))
>  			s = tab->name1;
>  		else
>  			s = tab->name0;
>  		if(s != nil && *s)
>  			n += snprint(buf+n, sizeof(buf)-n, ", %s", s);
>  	}
>  	if((debug & Dbginfo) && buf[0])
>  		fprint(2, "[%s]", buf+2);
>  }
>  
>  void
>  preport(Device *, int, ulong, byte *b, int n)
>  {
>  	byte *s, *es;
>  	int tag, nb, i, indent;
>  	int v;
>  	Flags *tab;
>  
>  	s = b+2;
>  	es = b+n;
>  	indent = 0;
>  	while(s < es){
>  		for(i=0; i<indent; i++)
>  			fprint(2, " ");
>  		tag = *s++;
>  		if(tag == Tlong){
>  			fprint(2, "long report tag");
>  			return;
>  		}
>  		if((nb = tag&3) == 3)
>  			nb = 4;
>  		v = 0;
>  		for(i=0; i<nb; i++)
>  			v |= s[i] << (i*8);
>  		switch(tag & Tmtype){
>  		case Treserved:
>  			if(tag == Tlong){
>  				fprint(2, "long report tag");
>  				return;
>  			}
>  			fprint(2, "illegal tag");
>  			return;
>  		case Tmain:
>  			tab = nil;
>  			if (debug & Dbginfo) {
>  				switch(tag & Tmitem){
>  				case Tinput:
>  					fprint(2, "Input");
>  					tab = ioflags;
>  					break;
>  				case Toutput:
>  					fprint(2, "Output");
>  					tab = ioflags;
>  					break;
>  				case Tfeature:
>  					fprint(2, "Feature");
>  					tab = ioflags;
>  					break;
>  				case Tcoll:
>  					fprint(2, "Collection");
>  					indent++;
>  					break;
>  				case Tecoll:
>  					fprint(2, "End Collection");
>  					indent--;
>  					break;
>  				default:
>  					fprint(2, "unexpected item %.2x", tag);
>  				}
>  				fprint(2, "=%#ux", v);
>  				if(tab != nil)
>  					pflag(tab, v);
>  			}
>  			break;
>  		case Tglobal:
>  			if (debug & Dbginfo) {
>  				fprint(2, "Global %#ux: ", v);
>  				switch(tag & Tmitem){
>  				case Tusagepage:
>  					fprint(2, "Usage Page %#ux", v);
>  					break;
>  				case Tlmin:
>  					fprint(2, "Logical Min %d", v);
>  					break;
>  				case Tlmax:
>  					fprint(2, "Logical Max %d", v);
>  					break;
>  				case Tpmin:
>  					fprint(2, "Physical Min %d", v);
>  					break;
>  				case Tpmax:
>  					fprint(2, "Physical Max %d", v);
>  					break;
>  				case Tunitexp:
>  					fprint(2, "Unit Exponent %d", v);
>  					break;
>  				case Tunit:
>  					fprint(2, "Unit %d", v);
>  					break;
>  				case Trepsize:
>  					fprint(2, "Report size %d", v);
>  					break;
>  				case TrepID:
>  					fprint(2, "Report ID %#x", v);
>  					break;
>  				case Trepcount:
>  					fprint(2, "Report Count %d", v);
>  					break;
>  				case Tpush:
>  					fprint(2, "Push %d", v);
>  					break;
>  				case Tpop:
>  					fprint(2, "Pop %d", v);
>  					break;
>  				default:
>  					fprint(2, "Unknown %#ux", v);
>  					break;
>  				}
>  			}
>  			break;
>  		case Tlocal:
>  			if (debug & Dbginfo) {
>  				fprint(2, "Local %#ux: ", v);
>  				switch(tag & Tmitem){
>  				case Tusage:
>  					fprint(2, "Usage %d", v);
>  					break;
>  				case Tumin:
>  					fprint(2, "Usage min %d", v);
>  					break;
>  				case Tumax:
>  					fprint(2, "Usage max %d", v);
>  					break;
>  				case Tdindex:
>  					fprint(2, "Designator index %d", v);
>  					break;
>  				case Tdmin:
>  					fprint(2, "Designator min %d", v);
>  					break;
>  				case Tdmax:
>  					fprint(2, "Designator max %d", v);
>  					break;
>  				case Tsindex:
>  					fprint(2, "String index %d", v);
>  					break;
>  				case Tsmin:
>  					fprint(2, "String min %d", v);
>  					break;
>  				case Tsmax:
>  					fprint(2, "String max %d", v);
>  					break;
>  				case Tsetdelim:
>  					fprint(2, "Set delim %#ux", v);
>  					break;
>  				default:
>  					fprint(2, "Unknown %#ux", v);
>  					break;
>  				}
>  			}
>  			break;
>  		}
>  		fprint(2, "\n");
>  		s += nb;
>  	}
>  }
>  
>  void
>  phub(Device *, int, ulong, void *b, int n)
>  {
>  	DHub *d;
>  
>  	if(n < DHUBLEN)
>  		return;
>  	d = b;
>  	if (debug & Dbginfo)
>  		fprint(2, "nport %d charac %#.4x pwr %dms current %dmA remov %#.2x",
>  			d->bNbrPorts, GET2(d->wHubCharacteristics),
>  			d->bPwrOn2PwrGood*2, d->bHubContrCurrent,
>  			d->DeviceRemovable[0]);
>  }
>  
>  void
>  pstring(Device *, int, ulong, void *b, int n)
>  {
>  	byte *rb;
>  	char *s;
>  	Rune r;
>  	int l;
>  
>  	if(n <= 2){
>  		fprint(2, "\"\"");
>  		return;
>  	}
>  	if(n & 1){
>  		fprint(2, "illegal count\n");
>  		return;
>  	}
>  	n = (n - 2)/2;
>  	rb = (byte*)b + 2;
>  	s = malloc(n*UTFmax+1);
>  	for(l=0; --n >= 0; rb += 2){
>  		r = GET2(rb);
>  		l += runetochar(s+l, &r);
>  	}
>  	s[l] = 0;
>  	fprint(2, "\"%s\"", s);
>  	free(s);
>  }
>  
>  void
>  pcs_raw(char *tag, byte *b, int n)
>  {
>  	int i;
>  
>  	if (debug & Dbginfo) {
>  		fprint(2, "%s", tag);
>  		for(i=2; i<n; i++)
>  			fprint(2, " %.2x", b[i]);
>  	}
>  }
>  
>  static void
>  pcs_config(Device *, int, ulong, void *b, int n)
>  {
>  	pcs_raw("CS_CONFIG", b, n);
>  }
>  
>  static void
>  pcs_string(Device *, ulong, void *b, int n)
>  {
>  	pcs_raw("CS_STRING", b, n);
>  }
>  
>  static void
>  pcs_endpoint(Device *, int, ulong, void *bb, int n)
>  {
>  	byte *b = bb;
>  
>  	if (debug & Dbginfo) {
>  		switch(b[2]) {
>  		case 0x01:
>  			fprint(2,
>  		"CS_ENDPOINT for TerminalID %d, delay %d, format_tag %#ux\n",
>  				b[3], b[4], b[5] | (b[6]<<8));
>  			break;
>  		case 0x02:
>  			fprint(2,
>  "CS_INTERFACE FORMAT_TYPE %d, channels %d, subframesize %d, resolution %d, freqtype %d, ",
>  				b[3], b[4], b[5], b[6], b[7]);
>  			fprint(2, "freq0 %d, freq1 %d\n",
>  				b[8] | b[9]<<8 | b[10]<<16,
>  				b[11] | b[12]<<8 | b[13]<<16);
>  			break;
>  		default:
>  			pcs_raw("CS_INTERFACE", bb, n);
>  		}
>  	}
>  }
>  
>  static void
>  pcs_interface(Device *, int n, ulong, void *bb, int nb)
>  {
>  
>  	if ((debug & Dbginfo) && n >= 0)
>  		pcs_raw("CS_INTERFACE", bb, nb);
>  }
>  
>  void
>  pdesc(Device *d, int c, ulong csp, byte *b, int n)
>  {
>  	int class, subclass, proto, dalt, i, ep, ifc, len;
>  	DConfig *dc;
>  	DEndpoint *de;
>  	DInterface *di;
>  	Dinf *dif;
>  	Endpt *dep;
>  	void (*f)(Device *, int, ulong, void*, int);
>  	char scbuf[64];
>  
>  	class = Class(csp);
>  	dalt = -1;
>  	ifc = -1;
>  	if (c >= nelem(d->config)) {
>  		fprint(2, "Too many interfaces (%d of %d)\n",
>  			c, nelem(d->config));
>  		return;
>  	}
>  	if(debug & Dbginfo)
>  		fprint(2, "pdesc %d.%d [%d]\n", d->id, c, n);
>  	len = -1;
>  	while(n > 2 && b[0] && b[0] <= n){
>  		if (debug & Dbginfo)
>  			fprint(2, "desc %d.%d [%d] %#2.2x: ", d->id, c, b[0], b[1]);
>  		switch (b[1]) {
>  		case CONFIGURATION:
>  			if(b[0] < DCONFLEN){
>  				if(debug & Dbginfo)
>  					fprint(2, "short config %d < %d", b[0], DCONFLEN);
>  				return;
>  			}
>  			dc = (DConfig*)b;
>  			d->config[c]->nif = dc->bNumInterfaces;
>  			d->config[c]->cval = dc->bConfigurationValue;
>  			d->config[c]->attrib = dc->bmAttributes;
>  			d->config[c]->milliamps = dc->MaxPower*2;
>  			d->nif += d->config[c]->nif;
>  			if (debug & Dbginfo)
>  				fprint(2, "config %d: tdlen %d ninterface %d iconfig %d attr %#.2x power %dmA\n",
>  					dc->bConfigurationValue,
>  					GET2(dc->wTotalLength),
>  					dc->bNumInterfaces, dc->iConfiguration,
>  					dc->bmAttributes, dc->MaxPower*2);
>  			break;
>  		case INTERFACE:
>  			if(n < DINTERLEN){
>  				if(debug & Dbginfo)
>  					fprint(2, "short interface %d < %d", b[0], DINTERLEN);
>  				return;
>  			}
>  			di = (DInterface *)b;
>  			class = di->bInterfaceClass;
>  			subclass = di->bInterfaceSubClass;
>  			proto = di->bInterfaceProtocol;
>  			csp = CSP(class, subclass, proto);
>  			if(debug & Dbginfo){
>  				sclass(scbuf, scbuf + sizeof scbuf, csp);
>  				fprint(2, "interface %d: alt %d nept %d class %#x subclass %#x proto %d [%s] iinterface %d\n",
>  					di->bInterfaceNumber,
>  					di->bAlternateSetting,
>  					di->bNumEndpoints, class, subclass,
>  					proto, scbuf, di->iInterface);
>  			}
>  			if (c < 0) {
>  				fprint(2, "Unexpected INTERFACE message\n");
>  				return;
>  			}
>  			ifc = di->bInterfaceNumber;
>  			dalt = di->bAlternateSetting;
>  			if (ifc < 0 || ifc >= nelem(d->config[c]->iface))
>  				sysfatal("Bad interface number %d", ifc);
>  			if (dalt < 0 ||
>  			 dalt >= nelem(d->config[c]->iface[ifc]->dalt))
>  				sysfatal("Bad alternate number %d", dalt);
>  			if (d->config[c] == nil)
>  				sysfatal("No config");
>  			if (ifc == 0) {
>  				if (c == 0)
>  					d->ep[0]->csp = csp;
>  				d->config[c]->csp = csp;
>  			}
>  			dif = d->config[c]->iface[ifc];
>  			if (dif == nil) {
>  				d->config[c]->iface[ifc] = dif =
>  					mallocz(sizeof(Dinf), 1);
>  				dif->csp = csp;
>  			}
>  			dif->interface = di->bInterfaceNumber;
>  			break;
>  		case ENDPOINT:
>  			if(n < DENDPLEN)
>  				return;
>  			de = (DEndpoint *)b;
>  			if(debug & Dbginfo) {
>  				fprint(2, "addr %#2.2x attrib %#2.2x maxpkt %d interval %dms",
>  					de->bEndpointAddress, de->bmAttributes,
>  					GET2(de->wMaxPacketSize), de->bInterval);
>  				if(de->bEndpointAddress & 0x80)
>  					fprint(2, " [IN]");
>  				else
>  					fprint(2, " [OUT]");
>  				switch(de->bmAttributes&0x33){
>  				case 0:
>  					fprint(2, " [Control]");
>  					break;
>  				case 1:
>  					fprint(2, " [Iso]");
>  					switch(de->bmAttributes&0xc){
>  					case 0x4:
>  						fprint(2, " [Asynchronous]");
>  						break;
>  					case 0x8:
>  						fprint(2, " [Adaptive]");
>  						break;
>  					case 0xc:
>  						fprint(2, " [Synchronous]");
>  						break;
>  					}
>  					break;
>  				case 2:
>  					fprint(2, " [Bulk]");
>  					break;
>  				case 3:
>  					fprint(2, " [Interrupt]");
>  					break;
>  				}
>  				if(b[0] == 9)
>  					fprint(2, "refresh %d synchaddress %d",
>  						b[7], b[8]);
>  				fprint(2, "\n");
>  			}
>  			if (c < 0 || ifc < 0 || dalt < 0) {
>  				fprint(2, "Unexpected ENDPOINT message\n");
>  				return;
>  			}
>  			dif = d->config[c]->iface[ifc];
>  			if (dif == nil)
>  				sysfatal("d->config[%d]->iface[%d] == nil",
>  					c, ifc);
>  			if (dif->dalt[dalt] == nil)
>  				dif->dalt[dalt] = mallocz(sizeof(Dalt),1);
>  			dif->dalt[dalt]->attrib = de->bmAttributes;
>  			dif->dalt[dalt]->interval = de->bInterval;
>  			ep = de->bEndpointAddress & 0xf;
>  			dep = d->ep[ep];
>  			if(debug)
>  				fprint(2, "%s: endpoint addr %d\n", argv0, ep);
>  			if(dep == nil){
>  				d->ep[ep] = dep = newendpt(d, ep, class);
>  				dep->dir = de->bEndpointAddress & 0x80
>  					? Ein : Eout;
>  			}else if ((dep->addr&0x80) !=
>  			 (de->bEndpointAddress&0x80))
>  				dep->dir = Eboth;
>  			dep->maxpkt = GET2(de->wMaxPacketSize);
>  			dep->addr = de->bEndpointAddress;
>  			dep->type = de->bmAttributes & 0x03;
>  			dep->isotype = (de->bmAttributes>>2) & 0x03;
>  			dep->csp = csp;
>  			dep->conf = d->config[c];
>  			dep->iface = dif;
>  			for(i = 0; i < nelem(dif->endpt); i++)
>  				if(dif->endpt[i] == nil){
>  					dif->endpt[i] = dep;
>  					break;
>  				}
>  			if(i == nelem(dif->endpt))
>  				fprint(2, "Too many endpoints\n");
>  			if (d->nif <= ep)
>  				d->nif = ep+1;
>  			break;
>  		default:
>  			assert(nelem(dprinter) == 0x100);
>  			f = dprinter[b[1]];
>  			if(f != nil){
>  				(*f)(d, c, dalt<<24 | ifc<<16 | (csp&0xffff),
>  					b, b[0]);
>  				if(debug & Dbginfo)
>  					fprint(2, "\n");
>  			}else
>  				if(verbose){
>  					int i;
>  
>  					switch(b[1]){
>  					case DEVICE:
>  						fprint(2, "(device)");
>  						break;
>  					case STRING:
>  						fprint(2, "(string)");
>  						break;
>  					default:
>  						fprint(2, "(unknown type)");
>  					}
>  					for(i=1; i<b[0]; i++)
>  						fprint(2, " %.2x", b[i]);
>  					fprint(2, "\n");
>  				}else if(debug & Dbginfo)
>  					fprint(2, "\n");
>  			break;
>  		}
>  		len = b[0];
>  		n -= len;
>  		b += len;
>  	}
>  	if(n)
>  		fprint(2, "pdesc: %d bytes left unparsed, b[0]=%d, b[-len]=%d, len=%d\n", n, b[0], b[-len], len);	
>  }


  reply	other threads:[~2008-02-12 14:42 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-02-12  1:36 erik quanstrom
2008-02-12  8:20 ` Richard Miller
2008-02-12 13:59 ` Sape Mullender
2008-02-12 14:13   ` erik quanstrom
2008-02-12 14:35     ` Sape Mullender
2008-02-12 14:42       ` Fco. J. Ballesteros [this message]
2008-02-13  0:50       ` erik quanstrom
2008-02-13 14:32         ` Richard Miller
2008-02-13 14:45           ` erik quanstrom
2008-02-13 14:52             ` Richard Miller
2008-02-13 15:04               ` erik quanstrom
2008-02-13 16:18             ` Sape Mullender
2008-02-13 16:37               ` erik quanstrom
2008-02-13 17:09                 ` Sape Mullender
2008-02-12  8:21 Richard Miller
2008-02-12 13:21 ` erik quanstrom
2008-02-12 21:24   ` Richard Miller
2008-02-12 21:28     ` geoff
2008-02-12 21:33       ` Richard Miller
2008-02-12 21:37     ` erik quanstrom
2008-02-12 22:18       ` Richard Miller
2008-02-12 23:05         ` geoff
2008-02-13  6:31           ` arisawa
2008-02-13  8:25             ` Richard Miller
2008-02-13  8:54               ` arisawa
2008-02-13 10:58             ` Richard Miller
2008-02-13 14:13               ` Richard Miller
2008-02-13 18:49                 ` geoff
2008-02-13 15:54           ` Richard Miller

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=e9cf4f97d406fc40f6ab5c21d6103125@lsub.org \
    --to=nemo@lsub.org \
    --cc=9fans@cse.psu.edu \
    /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).