Commit 8f122be8 authored by Luca Risolia's avatar Luca Risolia Committed by Greg Kroah-Hartman

[PATCH] USB: SN9C10x driver updates

SN9C10x driver updates.

Changes:

@ Fix the sysfs interface
@ Fix allocated minor number after device detection
+ Add "force_munmap" module parameter
+ Documentation updates
+ Add support for old VIDIOC_S_PARM_OLD and VIDIOC_S_CTRL_OLD ioctl's
Signed-off-by: default avatarLuca Risolia <luca.risolia@studio.unibo.it>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 2abe301b
...@@ -26,7 +26,7 @@ Index ...@@ -26,7 +26,7 @@ Index
1. Copyright 1. Copyright
============ ============
Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>
2. Disclaimer 2. Disclaimer
...@@ -165,6 +165,17 @@ Description: Specify V4L2 minor mode number: ...@@ -165,6 +165,17 @@ Description: Specify V4L2 minor mode number:
other camera. other camera.
Default: -1 Default: -1
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Name: force_munmap;
Type: bool array (min = 0, max = 64)
Syntax: <0|1[,...]>
Description: Force the application to unmap previously mapped buffer memory
before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not
all the applications support this feature. This parameter is
specific for each detected camera.
0 = do not force memory unmapping"
1 = force memory unmapping (save memory)"
Default: 0
-------------------------------------------------------------------------------
Name: debug Name: debug
Type: int Type: int
Syntax: <n> Syntax: <n>
...@@ -362,11 +373,11 @@ rules: ...@@ -362,11 +373,11 @@ rules:
file descriptor. Once it is selected, the application must close and reopen the file descriptor. Once it is selected, the application must close and reopen the
device to switch to the other I/O method; device to switch to the other I/O method;
- previously mapped buffer memory must always be unmapped before calling any - although it is not mandatory, previously mapped buffer memory should always
of the "VIDIOC_S_CROP", "VIDIOC_TRY_FMT" and "VIDIOC_S_FMT" ioctl's. The same be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's.
number of buffers as before will be allocated again to match the size of the The same number of buffers as before will be allocated again to match the size
new video frames, so you have to map the buffers again before any I/O attempts of the new video frames, so you have to map the buffers again before any I/O
on them. attempts on them.
Consistently with the hardware limits, this driver also supports image Consistently with the hardware limits, this driver also supports image
downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions. downscaling with arbitrary scaling factors from 1, 2 and 4 in both directions.
......
/*************************************************************************** /***************************************************************************
* V4L2 driver for SN9C10x PC Camera Controllers * * V4L2 driver for SN9C10x PC Camera Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#define SN9C102_DEBUG_LEVEL 2 #define SN9C102_DEBUG_LEVEL 2
#define SN9C102_MAX_DEVICES 64 #define SN9C102_MAX_DEVICES 64
#define SN9C102_PRESERVE_IMGSCALE 0 #define SN9C102_PRESERVE_IMGSCALE 0
#define SN9C102_FORCE_MUNMAP 0
#define SN9C102_MAX_FRAMES 32 #define SN9C102_MAX_FRAMES 32
#define SN9C102_URBS 2 #define SN9C102_URBS 2
#define SN9C102_ISO_PACKETS 7 #define SN9C102_ISO_PACKETS 7
...@@ -55,8 +56,8 @@ ...@@ -55,8 +56,8 @@
#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia"
#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
#define SN9C102_MODULE_LICENSE "GPL" #define SN9C102_MODULE_LICENSE "GPL"
#define SN9C102_MODULE_VERSION "1:1.20" #define SN9C102_MODULE_VERSION "1:1.22"
#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 20) #define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 22)
enum sn9c102_bridge { enum sn9c102_bridge {
BRIDGE_SN9C101 = 0x01, BRIDGE_SN9C101 = 0x01,
...@@ -109,6 +110,10 @@ struct sn9c102_sysfs_attr { ...@@ -109,6 +110,10 @@ struct sn9c102_sysfs_attr {
sn9c102_sof_header_t frame_header; sn9c102_sof_header_t frame_header;
}; };
struct sn9c102_module_param {
u8 force_munmap;
};
static DECLARE_MUTEX(sn9c102_sysfs_lock); static DECLARE_MUTEX(sn9c102_sysfs_lock);
static DECLARE_RWSEM(sn9c102_disconnect); static DECLARE_RWSEM(sn9c102_disconnect);
...@@ -138,6 +143,8 @@ struct sn9c102_device { ...@@ -138,6 +143,8 @@ struct sn9c102_device {
sn9c102_sof_header_t sof_header; sn9c102_sof_header_t sof_header;
u16 reg[32]; u16 reg[32];
struct sn9c102_module_param module_param;
enum sn9c102_dev_state state; enum sn9c102_dev_state state;
u8 users; u8 users;
......
/*************************************************************************** /***************************************************************************
* V4L2 driver for SN9C10x PC Camera Controllers * * V4L2 driver for SN9C10x PC Camera Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/page-flags.h> #include <linux/page-flags.h>
#include <linux/byteorder/generic.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -65,6 +66,20 @@ MODULE_PARM_DESC(video_nr, ...@@ -65,6 +66,20 @@ MODULE_PARM_DESC(video_nr,
"\none and for every other camera." "\none and for every other camera."
"\n"); "\n");
static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] =
SN9C102_FORCE_MUNMAP};
module_param_array(force_munmap, bool, NULL, 0444);
MODULE_PARM_DESC(force_munmap,
"\n<0|1[,...]> Force the application to unmap previously "
"\nmapped buffer memory before calling any VIDIOC_S_CROP or "
"\nVIDIOC_S_FMT ioctl's. Not all the applications support "
"\nthis feature. This parameter is specific for each "
"\ndetected camera."
"\n 0 = do not force memory unmapping"
"\n 1 = force memory unmapping (save memory)"
"\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
"\n");
#ifdef SN9C102_DEBUG #ifdef SN9C102_DEBUG
static unsigned short debug = SN9C102_DEBUG_LEVEL; static unsigned short debug = SN9C102_DEBUG_LEVEL;
module_param(debug, ushort, 0644); module_param(debug, ushort, 0644);
...@@ -141,10 +156,16 @@ static void rvfree(void* mem, size_t size) ...@@ -141,10 +156,16 @@ static void rvfree(void* mem, size_t size)
} }
static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count) static u32
sn9c102_request_buffers(struct sn9c102_device* cam, u32 count,
enum sn9c102_io_method io)
{ {
struct v4l2_pix_format* p = &(cam->sensor->pix_format); struct v4l2_pix_format* p = &(cam->sensor->pix_format);
const size_t imagesize = (p->width * p->height * p->priv)/8; struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
const size_t imagesize = cam->module_param.force_munmap ||
io == IO_READ ?
(p->width * p->height * p->priv)/8 :
(r->width * r->height * p->priv)/8;
void* buff = NULL; void* buff = NULL;
u32 i; u32 i;
...@@ -911,11 +932,6 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) ...@@ -911,11 +932,6 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
return -ENODEV; return -ENODEV;
} }
if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
up(&sn9c102_sysfs_lock);
return -ENOSYS;
}
value = sn9c102_strtou8(buf, len, &count); value = sn9c102_strtou8(buf, len, &count);
if (!count) { if (!count) {
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -1047,6 +1063,11 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) ...@@ -1047,6 +1063,11 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
return -ENODEV; return -ENODEV;
} }
if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
up(&sn9c102_sysfs_lock);
return -ENOSYS;
}
value = sn9c102_strtou8(buf, len, &count); value = sn9c102_strtou8(buf, len, &count);
if (!count) { if (!count) {
up(&sn9c102_sysfs_lock); up(&sn9c102_sysfs_lock);
...@@ -1514,7 +1535,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) ...@@ -1514,7 +1535,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
} }
if (cam->io == IO_NONE) { if (cam->io == IO_NONE) {
if (!sn9c102_request_buffers(cam, cam->nreadbuffers)) { if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
DBG(1, "read() failed, not enough memory") DBG(1, "read() failed, not enough memory")
up(&cam->fileop_sem); up(&cam->fileop_sem);
return -ENOMEM; return -ENOMEM;
...@@ -1594,7 +1615,7 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) ...@@ -1594,7 +1615,7 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
} }
if (cam->io == IO_NONE) { if (cam->io == IO_NONE) {
if (!sn9c102_request_buffers(cam, 2)) { if (!sn9c102_request_buffers(cam, 2, IO_READ)) {
DBG(1, "poll() failed, not enough memory") DBG(1, "poll() failed, not enough memory")
goto error; goto error;
} }
...@@ -1811,6 +1832,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1811,6 +1832,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return err; return err;
} }
case VIDIOC_S_CTRL_OLD:
case VIDIOC_S_CTRL: case VIDIOC_S_CTRL:
{ {
struct sn9c102_sensor* s = cam->sensor; struct sn9c102_sensor* s = cam->sensor;
...@@ -1895,12 +1917,13 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1895,12 +1917,13 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL; return -EINVAL;
for (i = 0; i < cam->nbuffers; i++) if (cam->module_param.force_munmap)
if (cam->frame[i].vma_use_count) { for (i = 0; i < cam->nbuffers; i++)
DBG(3, "VIDIOC_S_CROP failed. " if (cam->frame[i].vma_use_count) {
"Unmap the buffers first.") DBG(3, "VIDIOC_S_CROP failed. "
return -EINVAL; "Unmap the buffers first.")
} return -EINVAL;
}
/* Preserve R,G or B origin */ /* Preserve R,G or B origin */
rect->left = (s->_rect.left & 1L) ? rect->left = (s->_rect.left & 1L) ?
...@@ -1947,7 +1970,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1947,7 +1970,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
} }
sn9c102_release_buffers(cam); if (cam->module_param.force_munmap)
sn9c102_release_buffers(cam);
err = sn9c102_set_crop(cam, rect); err = sn9c102_set_crop(cam, rect);
if (s->set_crop) if (s->set_crop)
...@@ -1966,7 +1990,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -1966,7 +1990,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
s->pix_format.height = rect->height/scale; s->pix_format.height = rect->height/scale;
memcpy(&(s->_rect), rect, sizeof(*rect)); memcpy(&(s->_rect), rect, sizeof(*rect));
if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) { if (cam->module_param.force_munmap &&
nbuffers != sn9c102_request_buffers(cam, nbuffers,
cam->io)) {
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_CROP failed because of not enough " DBG(1, "VIDIOC_S_CROP failed because of not enough "
"memory. To use the camera, close and open " "memory. To use the camera, close and open "
...@@ -2103,12 +2129,13 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2103,12 +2129,13 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return 0; return 0;
} }
for (i = 0; i < cam->nbuffers; i++) if (cam->module_param.force_munmap)
if (cam->frame[i].vma_use_count) { for (i = 0; i < cam->nbuffers; i++)
DBG(3, "VIDIOC_S_FMT failed. " if (cam->frame[i].vma_use_count) {
"Unmap the buffers first.") DBG(3, "VIDIOC_S_FMT failed. "
return -EINVAL; "Unmap the buffers first.")
} return -EINVAL;
}
if (cam->stream == STREAM_ON) if (cam->stream == STREAM_ON)
if ((err = sn9c102_stream_interrupt(cam))) if ((err = sn9c102_stream_interrupt(cam)))
...@@ -2119,7 +2146,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2119,7 +2146,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return -EFAULT; return -EFAULT;
} }
sn9c102_release_buffers(cam); if (cam->module_param.force_munmap)
sn9c102_release_buffers(cam);
err += sn9c102_set_pix_format(cam, pix); err += sn9c102_set_pix_format(cam, pix);
err += sn9c102_set_crop(cam, &rect); err += sn9c102_set_crop(cam, &rect);
...@@ -2140,7 +2168,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2140,7 +2168,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
memcpy(pfmt, pix, sizeof(*pix)); memcpy(pfmt, pix, sizeof(*pix));
memcpy(&(s->_rect), &rect, sizeof(rect)); memcpy(&(s->_rect), &rect, sizeof(rect));
if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) { if (cam->module_param.force_munmap &&
nbuffers != sn9c102_request_buffers(cam, nbuffers,
cam->io)) {
cam->state |= DEV_MISCONFIGURED; cam->state |= DEV_MISCONFIGURED;
DBG(1, "VIDIOC_S_FMT failed because of not enough " DBG(1, "VIDIOC_S_FMT failed because of not enough "
"memory. To use the camera, close and open " "memory. To use the camera, close and open "
...@@ -2228,7 +2258,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2228,7 +2258,8 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
sn9c102_release_buffers(cam); sn9c102_release_buffers(cam);
if (rb.count) if (rb.count)
rb.count = sn9c102_request_buffers(cam, rb.count); rb.count = sn9c102_request_buffers(cam, rb.count,
IO_MMAP);
if (copy_to_user(arg, &rb, sizeof(rb))) { if (copy_to_user(arg, &rb, sizeof(rb))) {
sn9c102_release_buffers(cam); sn9c102_release_buffers(cam);
...@@ -2402,6 +2433,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, ...@@ -2402,6 +2433,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
return 0; return 0;
} }
case VIDIOC_S_PARM_OLD:
case VIDIOC_S_PARM: case VIDIOC_S_PARM:
{ {
struct v4l2_streamparm sp; struct v4l2_streamparm sp;
...@@ -2496,8 +2528,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2496,8 +2528,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]); n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
for (i = 0; i < n-1; i++) for (i = 0; i < n-1; i++)
if (le16_to_cpu(udev->descriptor.idVendor) == sn9c102_id_table[i].idVendor && if (le16_to_cpu(udev->descriptor.idVendor) ==
le16_to_cpu(udev->descriptor.idProduct) == sn9c102_id_table[i].idProduct) sn9c102_id_table[i].idVendor &&
le16_to_cpu(udev->descriptor.idProduct) ==
sn9c102_id_table[i].idProduct)
break; break;
if (i == n-1) if (i == n-1)
return -ENODEV; return -ENODEV;
...@@ -2596,6 +2630,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) ...@@ -2596,6 +2630,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor) DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor)
cam->module_param.force_munmap = force_munmap[dev_nr];
dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
sn9c102_create_sysfs(cam); sn9c102_create_sysfs(cam);
DBG(2, "Optional device control through 'sysfs' interface ready") DBG(2, "Optional device control through 'sysfs' interface ready")
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera * * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera * * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera * * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
......
/*************************************************************************** /***************************************************************************
* API for image sensors connected to the SN9C10x PC Camera Controllers * * API for image sensors connected to the SN9C10x PC Camera Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera * * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera * * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera *
* Controllers * * Controllers *
* * * *
* Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> * * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it> *
* * * *
* This program is free software; you can redistribute it and/or modify * * This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by * * it under the terms of the GNU General Public License as published by *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment