git: 139144a7ae7c - stable/13 - wsp(4): Add evdev support.

Vladimir Kondratyev wulf at FreeBSD.org
Wed Sep 8 00:02:40 UTC 2021


The branch stable/13 has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=139144a7ae7c72eda955b1fbc5e31f5feb7a403a

commit 139144a7ae7c72eda955b1fbc5e31f5feb7a403a
Author:     Vladimir Kondratyev <wulf at FreeBSD.org>
AuthorDate: 2021-08-24 23:01:42 +0000
Commit:     Vladimir Kondratyev <wulf at FreeBSD.org>
CommitDate: 2021-09-08 00:01:07 +0000

    wsp(4): Add evdev support.
    
    Reviewed by:    hselasky
    Tested by:      Greg V, Constantin Furst<constantin_AT_fuersten_DOT_info>
    PR:             252236
    Differential revision:  https://reviews.freebsd.org/D31653
    
    (cherry picked from commit 8d73071c47ff1f911bdaec6356f37feb4e3b7cb5)
---
 sys/dev/usb/input/wsp.c      | 305 ++++++++++++++++++++++++++++++++++++++++---
 sys/modules/usb/wsp/Makefile |   4 +-
 2 files changed, 287 insertions(+), 22 deletions(-)

diff --git a/sys/dev/usb/input/wsp.c b/sys/dev/usb/input/wsp.c
index 29ae36f1687d..526f4a0011c1 100644
--- a/sys/dev/usb/input/wsp.c
+++ b/sys/dev/usb/input/wsp.c
@@ -29,6 +29,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_evdev.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -56,6 +58,11 @@ __FBSDID("$FreeBSD$");
 #define	USB_DEBUG_VAR wsp_debug
 #include <dev/usb/usb_debug.h>
 
+#ifdef EVDEV_SUPPORT
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+#endif
+
 #include <sys/mouse.h>
 
 #define	WSP_DRIVER_NAME "wsp"
@@ -283,9 +290,14 @@ struct tp_finger {
 } __packed;
 
 /* trackpad finger data size, empirically at least ten fingers */
+#ifdef EVDEV_SUPPORT
+#define	MAX_FINGERS		MAX_MT_SLOTS
+#else
 #define	MAX_FINGERS		16
+#endif
 #define	SIZEOF_FINGER		sizeof(struct tp_finger)
 #define	SIZEOF_ALL_FINGERS	(MAX_FINGERS * SIZEOF_FINGER)
+#define	MAX_FINGER_ORIENTATION	16384
 
 #if (WSP_BUFFER_MAX < ((MAX_FINGERS * FSIZE_TYPE4) + FINGER_TYPE4))
 #error "WSP_BUFFER_MAX is too small"
@@ -308,50 +320,147 @@ enum {
 	WSP_FLAG_MAX,
 };
 
+/* device-specific parameters */
+struct wsp_param {
+	int snratio;			/* signal-to-noise ratio */
+	int min;			/* device minimum reading */
+	int max;			/* device maximum reading */
+	int size;			/* physical size, mm */
+};
+
 /* device-specific configuration */
 struct wsp_dev_params {
 	const struct wsp_tp* tp;
+	struct wsp_param p;		/* finger pressure limits */
+	struct wsp_param w;		/* finger width limits */
+	struct wsp_param x;		/* horizontal limits */
+	struct wsp_param y;		/* vertical limits */
+	struct wsp_param o;		/* orientation limits */
 };
 
+/* logical signal quality */
+#define	SN_PRESSURE	45		/* pressure signal-to-noise ratio */
+#define	SN_WIDTH	25		/* width signal-to-noise ratio */
+#define	SN_COORD	250		/* coordinate signal-to-noise ratio */
+#define	SN_ORIENT	10		/* orientation signal-to-noise ratio */
+
 static const struct wsp_dev_params wsp_dev_params[WSP_FLAG_MAX] = {
 	[WSP_FLAG_WELLSPRING1] = {
 		.tp = wsp_tp + TYPE1,
+		.p = { SN_PRESSURE, 0, 256, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4824, 5342, 105 },
+		.y = { SN_COORD, -172, 5820, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING2] = {
 		.tp = wsp_tp + TYPE1,
+		.p = { SN_PRESSURE, 0, 256, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4824, 4824, 105 },
+		.y = { SN_COORD, -172, 4290, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING3] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4460, 5166, 105 },
+		.y = { SN_COORD, -75, 6700, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING4] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4620, 5140, 105 },
+		.y = { SN_COORD, -150, 6600, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING4A] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4616, 5112, 105 },
+		.y = { SN_COORD, -142, 5234, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING5] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4415, 5050, 105 },
+		.y = { SN_COORD, -55, 6680, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING6] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4620, 5140, 105 },
+		.y = { SN_COORD, -150, 6600, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING5A] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4750, 5280, 105 },
+		.y = { SN_COORD, -150, 6730, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING6A] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4620, 5140, 105 },
+		.y = { SN_COORD, -150, 6600, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING7] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4750, 5280, 105 },
+		.y = { SN_COORD, -150, 6730, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING7A] = {
 		.tp = wsp_tp + TYPE2,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4750, 5280, 105 },
+		.y = { SN_COORD, -150, 6730, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING8] = {
 		.tp = wsp_tp + TYPE3,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4620, 5140, 105 },
+		.y = { SN_COORD, -150, 6600, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 	[WSP_FLAG_WELLSPRING9] = {
 		.tp = wsp_tp + TYPE4,
+		.p = { SN_PRESSURE, 0, 300, 0 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -4828, 5345, 105 },
+		.y = { SN_COORD, -203, 6803, 75 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
 };
 #define	WSP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
@@ -440,12 +549,17 @@ struct wsp_softc {
 
 	const struct wsp_dev_params *sc_params;	/* device configuration */
 
+#ifdef EVDEV_SUPPORT
+	struct evdev_dev *sc_evdev;
+#endif
 	mousehw_t sc_hw;
 	mousemode_t sc_mode;
 	u_int	sc_pollrate;
 	mousestatus_t sc_status;
+	int	sc_fflags;
 	u_int	sc_state;
-#define	WSP_ENABLED	       0x01
+#define	WSP_ENABLED		0x01
+#define	WSP_EVDEV_OPENED	0x02
 
 	struct tp_finger *index[MAX_FINGERS];	/* finger index data */
 	int16_t	pos_x[MAX_FINGERS];	/* position array */
@@ -486,8 +600,8 @@ struct wsp_softc {
 /*
  * function prototypes
  */
-static usb_fifo_cmd_t wsp_start_read;
-static usb_fifo_cmd_t wsp_stop_read;
+static usb_fifo_cmd_t wsp_fifo_start_read;
+static usb_fifo_cmd_t wsp_fifo_stop_read;
 static usb_fifo_open_t wsp_open;
 static usb_fifo_close_t wsp_close;
 static usb_fifo_ioctl_t wsp_ioctl;
@@ -496,11 +610,20 @@ static struct usb_fifo_methods wsp_fifo_methods = {
 	.f_open = &wsp_open,
 	.f_close = &wsp_close,
 	.f_ioctl = &wsp_ioctl,
-	.f_start_read = &wsp_start_read,
-	.f_stop_read = &wsp_stop_read,
+	.f_start_read = &wsp_fifo_start_read,
+	.f_stop_read = &wsp_fifo_stop_read,
 	.basename[0] = WSP_DRIVER_NAME,
 };
 
+#ifdef EVDEV_SUPPORT
+static evdev_open_t wsp_ev_open;
+static evdev_close_t wsp_ev_close;
+static const struct evdev_methods wsp_evdev_methods = {
+	.ev_open = &wsp_ev_open,
+	.ev_close = &wsp_ev_close,
+};
+#endif
+
 /* device initialization and shutdown */
 static int wsp_enable(struct wsp_softc *sc);
 static void wsp_disable(struct wsp_softc *sc);
@@ -709,6 +832,56 @@ wsp_attach(device_t dev)
 	sc->sc_touch = WSP_UNTOUCH;
 	sc->scr_mode = WSP_SCR_NONE;
 
+#ifdef EVDEV_SUPPORT
+	sc->sc_evdev = evdev_alloc();
+	evdev_set_name(sc->sc_evdev, device_get_desc(dev));
+	evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
+	evdev_set_id(sc->sc_evdev, BUS_USB, uaa->info.idVendor,
+	    uaa->info.idProduct, 0);
+	evdev_set_serial(sc->sc_evdev, usb_get_serial(uaa->device));
+	evdev_set_methods(sc->sc_evdev, sc, &wsp_evdev_methods);
+	evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER);
+	evdev_support_event(sc->sc_evdev, EV_SYN);
+	evdev_support_event(sc->sc_evdev, EV_ABS);
+	evdev_support_event(sc->sc_evdev, EV_KEY);
+
+#define WSP_SUPPORT_ABS(evdev, code, param)				\
+	evdev_support_abs((evdev), (code), (param).min, (param).max,	\
+	((param).max - (param).min) / (param).snratio, 0,		\
+	(param).size != 0 ? ((param).max - (param).min) / (param).size : 0);
+
+	/* finger position */
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x);
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y);
+	/* finger pressure */
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
+	/* finger touch area */
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
+	/* finger approach area */
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w);
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w);
+	/* finger orientation */
+	WSP_SUPPORT_ABS(sc->sc_evdev, ABS_MT_ORIENTATION, sc->sc_params->o);
+	/* button properties */
+	evdev_support_key(sc->sc_evdev, BTN_LEFT);
+	if ((sc->sc_params->tp->caps & HAS_INTEGRATED_BUTTON) != 0)
+		evdev_support_prop(sc->sc_evdev, INPUT_PROP_BUTTONPAD);
+	/* Enable automatic touch assignment for type B MT protocol */
+	evdev_support_abs(sc->sc_evdev, ABS_MT_SLOT,
+	    0, MAX_FINGERS - 1, 0, 0, 0);
+	evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID,
+	    -1, MAX_FINGERS - 1, 0, 0, 0);
+	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK);
+	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL);
+	/* Synaptics compatibility events */
+	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT);
+
+	err = evdev_register(sc->sc_evdev);
+	if (err)
+		goto detach;
+#endif
+
 	return (0);
 
 detach:
@@ -730,6 +903,10 @@ wsp_detach(device_t dev)
 
 	usb_fifo_detach(&sc->sc_fifo);
 
+#ifdef EVDEV_SUPPORT
+	evdev_free(sc->sc_evdev);
+#endif
+
 	usbd_transfer_unsetup(sc->sc_xfer, WSP_N_TRANSFER);
 
 	mtx_destroy(&sc->sc_mutex);
@@ -756,6 +933,9 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error)
 	int rdz = 0;
 	int len;
 	int i;
+#ifdef EVDEV_SUPPORT
+	int slot = 0;
+#endif
 
 	wsp_runing_rangecheck(&tun);
 
@@ -824,8 +1004,31 @@ wsp_intr_callback(struct usb_xfer *xfer, usb_error_t error)
 			sc->pos_x[i] = f->abs_x;
 			sc->pos_y[i] = -f->abs_y;
 			sc->index[i] = f;
+#ifdef EVDEV_SUPPORT
+			if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE && f->touch_major != 0) {
+				union evdev_mt_slot slot_data = {
+					.id = slot,
+					.x = f->abs_x,
+					.y = params->y.min + params->y.max - f->abs_y,
+					.p = f->pressure,
+					.maj = f->touch_major << 1,
+					.min = f->touch_minor << 1,
+					.w_maj = f->tool_major << 1,
+					.w_min = f->tool_minor << 1,
+					.ori = params->o.max - f->orientation,
+				};
+				evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data);
+				slot++;
+			}
+#endif
 		}
 
+#ifdef EVDEV_SUPPORT
+		if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) {
+			evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt);
+			evdev_sync(sc->sc_evdev);
+		}
+#endif
 		sc->sc_status.flags &= ~MOUSE_POSCHANGED;
 		sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED;
 		sc->sc_status.obutton = sc->sc_status.button;
@@ -1130,9 +1333,8 @@ wsp_reset_buf(struct wsp_softc *sc)
 }
 
 static void
-wsp_start_read(struct usb_fifo *fifo)
+wsp_start_read(struct wsp_softc *sc)
 {
-	struct wsp_softc *sc = usb_fifo_softc(fifo);
 	int rate;
 
 	/* Check if we should override the default polling interval */
@@ -1153,49 +1355,109 @@ wsp_start_read(struct usb_fifo *fifo)
 }
 
 static void
-wsp_stop_read(struct usb_fifo *fifo)
+wsp_stop_read(struct wsp_softc *sc)
 {
-	struct wsp_softc *sc = usb_fifo_softc(fifo);
-
 	usbd_transfer_stop(sc->sc_xfer[WSP_INTR_DT]);
 }
 
 static int
 wsp_open(struct usb_fifo *fifo, int fflags)
 {
-	DPRINTFN(WSP_LLEVEL_INFO, "\n");
+	struct wsp_softc *sc = usb_fifo_softc(fifo);
+	int rc = 0;
 
-	if (fflags & FREAD) {
-		struct wsp_softc *sc = usb_fifo_softc(fifo);
-		int rc;
+	DPRINTFN(WSP_LLEVEL_INFO, "\n");
 
-		if (sc->sc_state & WSP_ENABLED)
-			return (EBUSY);
+	if (sc->sc_fflags & fflags)
+		return (EBUSY);
 
+	if (fflags & FREAD) {
 		if (usb_fifo_alloc_buffer(fifo,
 		    WSP_FIFO_BUF_SIZE, WSP_FIFO_QUEUE_MAXLEN)) {
 			return (ENOMEM);
 		}
-		rc = wsp_enable(sc);
+#ifdef EVDEV_SUPPORT
+		if ((sc->sc_state & WSP_EVDEV_OPENED) == 0)
+#endif
+			rc = wsp_enable(sc);
 		if (rc != 0) {
 			usb_fifo_free_buffer(fifo);
 			return (rc);
 		}
 	}
+	sc->sc_fflags |= fflags & (FREAD | FWRITE);
 	return (0);
 }
 
 static void
 wsp_close(struct usb_fifo *fifo, int fflags)
 {
-	if (fflags & FREAD) {
-		struct wsp_softc *sc = usb_fifo_softc(fifo);
+	struct wsp_softc *sc = usb_fifo_softc(fifo);
 
-		wsp_disable(sc);
+	if (fflags & FREAD) {
+#ifdef EVDEV_SUPPORT
+		if ((sc->sc_state & WSP_EVDEV_OPENED) == 0)
+#endif
+			wsp_disable(sc);
 		usb_fifo_free_buffer(fifo);
 	}
+
+	sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
+}
+
+static void
+wsp_fifo_start_read(struct usb_fifo *fifo)
+{
+	struct wsp_softc *sc = usb_fifo_softc(fifo);
+
+	wsp_start_read(sc);
+}
+
+static void
+wsp_fifo_stop_read(struct usb_fifo *fifo)
+{
+	struct wsp_softc *sc = usb_fifo_softc(fifo);
+
+#ifdef EVDEV_SUPPORT
+	if ((sc->sc_state & WSP_EVDEV_OPENED) == 0)
+#endif
+		wsp_stop_read(sc);
 }
 
+#ifdef EVDEV_SUPPORT
+static int
+wsp_ev_open(struct evdev_dev *evdev)
+{
+	struct wsp_softc *sc = evdev_get_softc(evdev);
+	int rc = 0;
+
+	mtx_lock(&sc->sc_mutex);
+	if (sc->sc_fflags == 0)
+		rc = wsp_enable(sc);
+	if (rc == 0) {
+		wsp_start_read(sc);
+		sc->sc_state |= WSP_EVDEV_OPENED;
+	}
+	mtx_unlock(&sc->sc_mutex);
+
+	return (rc);
+}
+
+static int
+wsp_ev_close(struct evdev_dev *evdev)
+{
+	struct wsp_softc *sc = evdev_get_softc(evdev);
+
+	mtx_lock(&sc->sc_mutex);
+	sc->sc_state &= ~WSP_EVDEV_OPENED;
+	if (sc->sc_fflags == 0)
+		wsp_stop_read(sc);
+	mtx_unlock(&sc->sc_mutex);
+
+	return (0);
+}
+#endif
+
 int
 wsp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
 {
@@ -1307,5 +1569,8 @@ static devclass_t wsp_devclass;
 DRIVER_MODULE(wsp, uhub, wsp_driver, wsp_devclass, NULL, 0);
 MODULE_DEPEND(wsp, usb, 1, 1, 1);
 MODULE_DEPEND(wsp, hid, 1, 1, 1);
+#ifdef EVDEV_SUPPORT
+MODULE_DEPEND(wsp, evdev, 1, 1, 1);
+#endif
 MODULE_VERSION(wsp, 1);
 USB_PNP_HOST_INFO(wsp_devs);
diff --git a/sys/modules/usb/wsp/Makefile b/sys/modules/usb/wsp/Makefile
index a5215c0150f5..73a289a09544 100644
--- a/sys/modules/usb/wsp/Makefile
+++ b/sys/modules/usb/wsp/Makefile
@@ -30,7 +30,7 @@ S=     ${SRCTOP}/sys
 .PATH: $S/dev/usb/input
 
 KMOD=	wsp
-SRCS=	opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h vnode_if.h usbdevs.h \
-	wsp.c
+SRCS=	opt_bus.h opt_evdev.h opt_usb.h device_if.h bus_if.h usb_if.h \
+	vnode_if.h usbdevs.h wsp.c
 
 .include <bsd.kmod.mk>


More information about the dev-commits-src-all mailing list