linux-hams.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ralf Baechle DL5RB <ralf@linux-mips.org>
To: linux-hams@vger.kernel.org
Subject: Another 6pack patch
Date: Tue, 2 Nov 2004 21:31:07 +0100	[thread overview]
Message-ID: <20041102203107.GA25455@linux-mips.org> (raw)

Below another 6pack patch which I've got sitting in my repository for a
while already.  I've got one positive report about it so far but would
like to give a little more testing before sending this upstream.  The
patch does:

 o Update my callsign
 o Cleanup the internal for synchronization with a 6pack TNC a little bit
   so it won't result in heaps and piles of messages in syslogs as it used
   to do.
 o Does paranoia checks for the maximum allowable packet size on transmit.
   There was a slight chance this might have resulted in a crash in just
   the right configuration.
 o Verifies the address family for the SIOCSIFHWADDR network interface
   ioctl.
 o Tries to do proper locking so nothing will access dev->dev_addr in
   midflight while changing the layer 2 address.
 o Uses an intermediate buffer for the SIOCSIFHWADDR ioctl so an error in
   copy_from_user can't result in a half-modified MAC address.
 o Swaps the arguments of decode_prio_command, decode_std_command and
   decode_data such that for consistency the struct sixpack pointer will be
   the first argument like everywhere else.


73 de DL5RB op Ralf

--
Loc. JN47BS / CQ 14 / ITU 28 / DOK A21


--- drivers/net/hamradio/6pack.c	2004-10-25 17:07:35.000000000 +0200
+++ drivers/net/hamradio/6pack.c	2004-11-02 19:51:04.738359534 +0100
@@ -4,7 +4,7 @@
  *		kernel's AX.25 protocol layers.
  *
  * Authors:	Andreas Könsgen <ajk@iehk.rwth-aachen.de>
- *              Ralf Baechle DO1GRB <ralf@linux-mips.org>
+ *              Ralf Baechle DL5RB <ralf@linux-mips.org>
  *
  * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
  *
@@ -119,11 +119,10 @@
 	unsigned char		status1;
 	unsigned char		status2;
 	unsigned char		tx_enable;
-	unsigned char		tnc_ok;
+	unsigned char		tnc_state;
 
 	struct timer_list	tx_t;
 	struct timer_list	resync_t;
-
 	atomic_t		refcnt;
 	struct semaphore	dead_sem;
 	spinlock_t		lock;
@@ -134,7 +133,6 @@
 static void sp_start_tx_timer(struct sixpack *);
 static void sixpack_decode(struct sixpack *, unsigned char[], int);
 static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
-static int sixpack_init(struct net_device *dev);
 
 /*
  * perform the persistence/slottime algorithm for CSMA access. If the
@@ -187,6 +185,11 @@
 		goto out_drop;
 	}
 
+	if (len > sp->mtu) {	/* sp->mtu = AX25_MTU = max. PACLEN = 256 */
+		msg = "oversized transmit packet!";
+		goto out_drop;
+	}
+
 	if (p[0] > 5) {
 		msg = "invalid KISS command";
 		goto out_drop;
@@ -249,8 +252,8 @@
 out_drop:
 	sp->stats.tx_dropped++;
 	netif_start_queue(sp->dev);
-	printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg);
-	return;
+	if (net_ratelimit())
+		printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg);
 }
 
 /* Encapsulate an IP datagram and kick it into a TTY queue. */
@@ -313,10 +316,20 @@
 	return &sp->stats;
 }
 
-static int sp_set_dev_mac_address(struct net_device *dev, void *addr)
+static int sp_set_mac_address(struct net_device *dev, void *addr)
 {
-	struct sockaddr *sa = addr;
-	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+	struct sockaddr_ax25 *sa = addr;
+
+	if (sa->sax25_family != AF_AX25)
+		return -EINVAL;
+
+	if (!sa->sax25_ndigis)
+		return -EINVAL;
+
+	spin_lock_irq(&dev->xmit_lock);
+	memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN);
+	spin_unlock_irq(&dev->xmit_lock);
+
 	return 0;
 }
 
@@ -337,7 +350,6 @@
 		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
 
 	/* Finish setting up the DEVICE info. */
-	dev->init		= sixpack_init;
 	dev->mtu		= SIXP_MTU;
 	dev->hard_start_xmit	= sp_xmit;
 	dev->open		= sp_open_dev;
@@ -345,7 +357,7 @@
 	dev->stop		= sp_close;
 	dev->hard_header	= sp_header;
 	dev->get_stats	        = sp_get_stats;
-	dev->set_mac_address    = sp_set_dev_mac_address;
+	dev->set_mac_address    = sp_set_mac_address;
 	dev->hard_header_len	= AX25_MAX_HEADER_LEN;
 	dev->addr_len		= AX25_ADDR_LEN;
 	dev->type		= ARPHRD_AX25;
@@ -359,51 +371,9 @@
 
 	SET_MODULE_OWNER(dev);
 
-	/* New-style flags. */
 	dev->flags		= 0;
 }
 
-/* Find a free 6pack channel, and link in this `tty' line. */
-static inline struct sixpack *sp_alloc(void)
-{
-	struct sixpack *sp = NULL;
-	struct net_device *dev = NULL;
-
-	dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup);
-	if (!dev)
-		return NULL;
-
-	sp = netdev_priv(dev);
-	sp->dev = dev;
-
-	spin_lock_init(&sp->lock);
-
-	if (register_netdev(dev))
-		goto out_free;
-
-	return sp;
-
-out_free:
-	printk(KERN_WARNING "sp_alloc() - register_netdev() failure.\n");
-
-	free_netdev(dev);
-
-	return NULL;
-}
-
-/* Free a 6pack channel. */
-static inline void sp_free(struct sixpack *sp)
-{
-	void * tmp;
-
-	/* Free all 6pack frame buffers. */
-	if ((tmp = xchg(&sp->rbuff, NULL)) != NULL)
-		kfree(tmp);
-	if ((tmp = xchg(&sp->xbuff, NULL)) != NULL)
-		kfree(tmp);
-}
-
-
 /* Send one completely decapsulated IP datagram to the IP layer. */
 
 /*
@@ -482,6 +452,8 @@
 	struct sixpack *sp = sp_get(tty);
 	int actual;
 
+	if (!sp)
+		return;
 	if (sp->xleft <= 0)  {
 		/* Now serial buffer is almost free & we can start
 		 * transmission of another packet */
@@ -492,7 +464,7 @@
 		goto out;
 	}
 
-	if (sp->tx_enable == 1) {
+	if (sp->tx_enable) {
 		actual = tty->driver->write(tty, sp->xhead, sp->xleft);
 		sp->xleft -= actual;
 		sp->xhead += actual;
@@ -504,80 +476,6 @@
 
 /* ----------------------------------------------------------------------- */
 
-/* Open the low-level part of the 6pack channel. */
-static int sp_open(struct net_device *dev)
-{
-	struct sixpack *sp = netdev_priv(dev);
-	char *rbuff, *xbuff = NULL;
-	int err = -ENOBUFS;
-	unsigned long len;
-
-	/* !!! length of the buffers. MTU is IP MTU, not PACLEN!  */
-
-	len = dev->mtu * 2;
-
-	rbuff = kmalloc(len + 4, GFP_KERNEL);
-	if (rbuff == NULL)
-		goto err_exit;
-
-	xbuff = kmalloc(len + 4, GFP_KERNEL);
-	if (xbuff == NULL)
-		goto err_exit;
-
-	spin_lock_bh(&sp->lock);
-
-	if (sp->tty == NULL)
-		return -ENODEV;
-
-	/*
-	 * Allocate the 6pack frame buffers:
-	 *
-	 * rbuff	Receive buffer.
-	 * xbuff	Transmit buffer.
-	 */
-
-	rbuff = xchg(&sp->rbuff, rbuff);
-	xbuff = xchg(&sp->xbuff, xbuff);
-
-	sp->mtu	     = AX25_MTU + 73;
-	sp->buffsize = len;
-	sp->rcount   = 0;
-	sp->rx_count = 0;
-	sp->rx_count_cooked = 0;
-	sp->xleft    = 0;
-
-	sp->flags	= 0;		/* Clear ESCAPE & ERROR flags */
-
-	sp->duplex = 0;
-	sp->tx_delay    = SIXP_TXDELAY;
-	sp->persistence = SIXP_PERSIST;
-	sp->slottime    = SIXP_SLOTTIME;
-	sp->led_state   = 0x60;
-	sp->status      = 1;
-	sp->status1     = 1;
-	sp->status2     = 0;
-	sp->tnc_ok      = 0;
-	sp->tx_enable   = 0;
-
-	netif_start_queue(dev);
-
-	init_timer(&sp->tx_t);
-	init_timer(&sp->resync_t);
-
-	spin_unlock_bh(&sp->lock);
-
-	err = 0;
-
-err_exit:
-	if (xbuff)
-		kfree(xbuff);
-	if (rbuff)
-		kfree(rbuff);
-
-	return err;
-}
-
-
 static int sixpack_receive_room(struct tty_struct *tty)
 {
 	return 65536;  /* We can handle an infinite amount of data. :-) */
@@ -629,14 +527,45 @@
  * decode_prio_command
  */
 
+#define TNC_UNINITIALIZED	0
+#define TNC_UNSYNC_STARTUP	1
+#define TNC_UNSYNCED		2
+#define TNC_IN_SYNC		3
+
+static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
+{
+	char *msg;
+
+	switch (new_tnc_state) {
+	default:			/* gcc oh piece-o-crap ... */
+	case TNC_UNSYNC_STARTUP:
+		msg = "Synchronizing with TNC";
+		break;
+	case TNC_UNSYNCED:
+		msg = "Lost synchronization with TNC\n";
+		break;
+	case TNC_IN_SYNC:
+		msg = "Found TNC";
+		break;
+	}
+
+	sp->tnc_state = new_tnc_state;
+	printk(KERN_INFO "%s: %s\n", sp->dev->name, msg);
+}
+
+static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
+{
+	int old_tnc_state = sp->tnc_state;
+
+	if (old_tnc_state != new_tnc_state)
+		__tnc_set_sync_state(sp, new_tnc_state);
+}
+
 static void resync_tnc(unsigned long channel)
 {
 	struct sixpack *sp = (struct sixpack *) channel;
-	struct net_device *dev = sp->dev;
 	static char resync_cmd = 0xe8;
 
-	printk(KERN_INFO "%s: resyncing TNC\n", dev->name);
-
 	/* clear any data that might have been received */
 
 	sp->rx_count = 0;
@@ -647,7 +576,6 @@
 	sp->status = 1;
 	sp->status1 = 1;
 	sp->status2 = 0;
-	sp->tnc_ok = 0;
 
 	/* resync the TNC */
 
@@ -659,9 +587,9 @@
 	/* Start resync timer again -- the TNC might be still absent */
 
 	del_timer(&sp->resync_t);
-	sp->resync_t.data = (unsigned long) sp;
-	sp->resync_t.function = resync_tnc;
-	sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
+	sp->resync_t.data	= (unsigned long) sp;
+	sp->resync_t.function	= resync_tnc;
+	sp->resync_t.expires	= jiffies + SIXP_RESYNC_TIMEOUT;
 	add_timer(&sp->resync_t);
 }
 
@@ -669,6 +597,8 @@
 {
 	unsigned char inbyte = 0xe8;
 
+	tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP);
+
 	sp->tty->driver->write(sp->tty, &inbyte, 1);
 
 	del_timer(&sp->resync_t);
@@ -689,31 +619,91 @@
  */
 static int sixpack_open(struct tty_struct *tty)
 {
+	char *rbuff = NULL, *xbuff = NULL;
+	struct net_device *dev;
 	struct sixpack *sp;
+	unsigned long len;
 	int err = 0;
 
 	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 
-	sp = sp_alloc();
-	if (!sp) {
+	dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup);
+	if (!dev) {
 		err = -ENOMEM;
 		goto out;
 	}
 
-	sp->tty = tty;
+	sp = netdev_priv(dev);
+	sp->dev = dev;
+
+	spin_lock_init(&sp->lock);
 	atomic_set(&sp->refcnt, 1);
 	init_MUTEX_LOCKED(&sp->dead_sem);
 
-	/* Perform the low-level 6pack initialization. */
-	if ((err = sp_open(sp->dev)))
-		goto out;
+	/* !!! length of the buffers. MTU is IP MTU, not PACLEN!  */
+
+	len = dev->mtu * 2;
+
+	rbuff = kmalloc(len + 4, GFP_KERNEL);
+	xbuff = kmalloc(len + 4, GFP_KERNEL);
+
+	if (rbuff == NULL || xbuff == NULL) {
+		err = -ENOBUFS;
+		goto out_free;
+	}
+
+	spin_lock_bh(&sp->lock);
+
+	sp->tty = tty;
+
+	sp->rbuff	= rbuff;
+	sp->xbuff	= xbuff;
+
+	sp->mtu		= AX25_MTU + 73;
+	sp->buffsize	= len;
+	sp->rcount	= 0;
+	sp->rx_count	= 0;
+	sp->rx_count_cooked = 0;
+	sp->xleft	= 0;
+
+	sp->flags	= 0;		/* Clear ESCAPE & ERROR flags */
+
+	sp->duplex	= 0;
+	sp->tx_delay    = SIXP_TXDELAY;
+	sp->persistence = SIXP_PERSIST;
+	sp->slottime    = SIXP_SLOTTIME;
+	sp->led_state   = 0x60;
+	sp->status      = 1;
+	sp->status1     = 1;
+	sp->status2     = 0;
+	sp->tx_enable   = 0;
+
+	netif_start_queue(dev);
+
+	init_timer(&sp->tx_t);
+	init_timer(&sp->resync_t);
+
+	spin_unlock_bh(&sp->lock);
 
 	/* Done.  We have linked the TTY line to a channel. */
 	tty->disc_data = sp;
 
+	/* Now we're ready to register. */
+	if (register_netdev(dev))
+		goto out_free;
+
 	tnc_init(sp);
 
+	return 0;
+
+out_free:
+	kfree(xbuff);
+	kfree(rbuff);
+
+	if (dev)
+		free_netdev(dev);
+
 out:
 	return err;
 }
@@ -727,7 +717,7 @@
  */
 static void sixpack_close(struct tty_struct *tty)
 {
-	struct sixpack *sp = (struct sixpack *) tty->disc_data;
+	struct sixpack *sp;
 
 	write_lock(&disc_data_lock);
 	sp = tty->disc_data;
@@ -743,16 +733,14 @@
 	if (!atomic_dec_and_test(&sp->refcnt))
 		down(&sp->dead_sem);
 
+	unregister_netdev(sp->dev);
+
 	del_timer(&sp->tx_t);
 	del_timer(&sp->resync_t);
 
-	sp_free(sp);
-	unregister_netdev(sp->dev);
-}
-
-static int sp_set_mac_address(struct net_device *dev, void __user *addr)
-{
-	return copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN) ? -EFAULT : 0;
+	/* Free all 6pack frame buffers. */
+	kfree(sp->rbuff);
+	kfree(sp->xbuff);
 }
 
 /* Perform I/O control on an active 6pack channel. */
@@ -760,6 +748,7 @@
 	unsigned int cmd, unsigned long arg)
 {
 	struct sixpack *sp = sp_get(tty);
+	struct net_device *dev = sp->dev;
 	unsigned int tmp, err;
 
 	if (!sp)
@@ -767,8 +756,8 @@
 
 	switch(cmd) {
 	case SIOCGIFNAME:
-		err = copy_to_user((void __user *) arg, sp->dev->name,
-		                   strlen(sp->dev->name) + 1) ? -EFAULT : 0;
+		err = copy_to_user((void *) arg, dev->name,
+		                   strlen(dev->name) + 1) ? -EFAULT : 0;
 		break;
 
 	case SIOCGIFENCAP:
@@ -782,16 +771,30 @@
 		}
 
 		sp->mode = tmp;
-		sp->dev->addr_len        = AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
-		sp->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
-		sp->dev->type            = ARPHRD_AX25;
+		dev->addr_len        = AX25_ADDR_LEN;
+		dev->hard_header_len = AX25_KISS_HEADER_LEN +
+		                       AX25_MAX_HEADER_LEN + 3;
+		dev->type            = ARPHRD_AX25;
 
 		err = 0;
 		break;
 
-	 case SIOCSIFHWADDR:
-		err = sp_set_mac_address(sp->dev, (void __user *) arg);
+	 case SIOCSIFHWADDR: {
+		char addr[AX25_ADDR_LEN];
+
+		if (copy_from_user(&addr,
+		                   (void __user *) arg, AX25_ADDR_LEN)) {
+			err = -EFAULT;
+			break;
+		}
+
+		spin_lock_irq(&dev->xmit_lock);
+		memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN);
+		spin_unlock_irq(&dev->xmit_lock);
+
+		err = 0;
 		break;
+	}
 
 	/* Allow stty to read, but not set, the serial port */
 	case TCGETS:
@@ -800,7 +803,7 @@
 		break;
 
 	default:
-		return -ENOIOCTLCMD;
+		err = -ENOIOCTLCMD;
 	}
 
 	sp_put(sp);
@@ -808,7 +811,6 @@
 	return err;
 }
 
-/* Fill in our line protocol discipline */
 static struct tty_ldisc sp_ldisc = {
 	.owner		= THIS_MODULE,
 	.magic		= TTY_LDISC_MAGIC,
@@ -823,8 +825,10 @@
 
 /* Initialize 6pack control device -- register 6pack line discipline */
 
-static char msg_banner[]  __initdata = KERN_INFO "AX.25: 6pack driver, " SIXPACK_VERSION "\n";
-static char msg_regfail[] __initdata = KERN_ERR  "6pack: can't register line discipline (err = %d)\n";
+static char msg_banner[]  __initdata = KERN_INFO \
+	"AX.25: 6pack driver, " SIXPACK_VERSION "\n";
+static char msg_regfail[] __initdata = KERN_ERR  \
+	"6pack: can't register line discipline (err = %d)\n";
 
 static int __init sixpack_init_driver(void)
 {
@@ -839,7 +843,8 @@
 	return status;
 }
 
-static const char msg_unregfail[] __exitdata = KERN_ERR "6pack: can't unregister line discipline (err = %d)\n";
+static const char msg_unregfail[] __exitdata = KERN_ERR \
+	"6pack: can't unregister line discipline (err = %d)\n";
 
 static void __exit sixpack_exit_driver(void)
 {
@@ -849,22 +854,6 @@
 		printk(msg_unregfail, ret);
 }
 
-/* Initialize the 6pack driver.  Called by DDI. */
-static int sixpack_init(struct net_device *dev)
-{
-	struct sixpack *sp = netdev_priv(dev);
-
-	if (sp == NULL)		/* Allocation failed ?? */
-		return -ENODEV;
-
-	/* Set up the "6pack Control Block". (And clear statistics) */
-
-	memset(sp, 0, sizeof (struct sixpack));
-	sp->dev	   = dev;
-
-	return 0;
-}
-
 /* encode an AX.25 packet into 6pack */
 
 static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw,
@@ -905,7 +894,7 @@
 
 /* decode 4 sixpack-encoded bytes into 3 data bytes */
 
-static void decode_data(unsigned char inbyte, struct sixpack *sp)
+static void decode_data(struct sixpack *sp, unsigned char inbyte)
 {
 	unsigned char *buf;
 
@@ -927,7 +916,7 @@
 
 /* identify and execute a 6pack priority command byte */
 
-static void decode_prio_command(unsigned char cmd, struct sixpack *sp)
+static void decode_prio_command(struct sixpack *sp, unsigned char cmd)
 {
 	unsigned char channel;
 	int actual;
@@ -971,11 +960,11 @@
         /* if the state byte has been received, the TNC is present,
            so the resync timer can be reset. */
 
-	if (sp->tnc_ok == 1) {
+	if (sp->tnc_state == TNC_IN_SYNC) {
 		del_timer(&sp->resync_t);
-		sp->resync_t.data = (unsigned long) sp;
-		sp->resync_t.function = resync_tnc;
-		sp->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT;
+		sp->resync_t.data	= (unsigned long) sp;
+		sp->resync_t.function	= resync_tnc;
+		sp->resync_t.expires	= jiffies + SIXP_INIT_RESYNC_TIMEOUT;
 		add_timer(&sp->resync_t);
 	}
 
@@ -984,7 +973,7 @@
 
 /* identify and execute a standard 6pack command byte */
 
-static void decode_std_command(unsigned char cmd, struct sixpack *sp)
+static void decode_std_command(struct sixpack *sp, unsigned char cmd)
 {
 	unsigned char checksum = 0, rest = 0, channel;
 	short i;
@@ -1005,7 +994,7 @@
 			rest = sp->rx_count;
 			if (rest != 0)
 				 for (i = rest; i <= 3; i++)
-					decode_data(0, sp);
+					decode_data(sp, 0);
 			if (rest == 2)
 				sp->rx_count_cooked -= 2;
 			else if (rest == 3)
@@ -1033,7 +1022,7 @@
 /* decode a 6pack packet */
 
 static void
-sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count)
+sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count)
 {
 	unsigned char inbyte;
 	int count1;
@@ -1041,16 +1030,15 @@
 	for (count1 = 0; count1 < count; count1++) {
 		inbyte = pre_rbuff[count1];
 		if (inbyte == SIXP_FOUND_TNC) {
-			printk(KERN_INFO "6pack: TNC found.\n");
-			sp->tnc_ok = 1;
+			tnc_set_sync_state(sp, TNC_IN_SYNC);
 			del_timer(&sp->resync_t);
 		}
 		if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
-			decode_prio_command(inbyte, sp);
+			decode_prio_command(sp, inbyte);
 		else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
-			decode_std_command(inbyte, sp);
+			decode_std_command(sp, inbyte);
 		else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
-			decode_data(inbyte, sp);
+			decode_data(sp, inbyte);
 	}
 }
 
-
To unsubscribe from this list: send the line "unsubscribe linux-hams" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

             reply	other threads:[~2004-11-02 20:31 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-11-02 20:31 Ralf Baechle DL5RB [this message]
2004-11-03  4:43 ` AX.25 cb lookup patch Ralf Baechle DL5RB

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=20041102203107.GA25455@linux-mips.org \
    --to=ralf@linux-mips.org \
    --cc=linux-hams@vger.kernel.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).