Skip to content
Snippets Groups Projects
Commit 902865c3 authored by Florian Pose's avatar Florian Pose
Browse files

Added 'pdos' command to ethercat tool.

parent 8444b2af
No related branches found
No related tags found
No related merge requests found
...@@ -52,15 +52,15 @@ int eccdev_open(struct inode *, struct file *); ...@@ -52,15 +52,15 @@ int eccdev_open(struct inode *, struct file *);
int eccdev_release(struct inode *, struct file *); int eccdev_release(struct inode *, struct file *);
ssize_t eccdev_read(struct file *, char __user *, size_t, loff_t *); ssize_t eccdev_read(struct file *, char __user *, size_t, loff_t *);
ssize_t eccdev_write(struct file *, const char __user *, size_t, loff_t *); ssize_t eccdev_write(struct file *, const char __user *, size_t, loff_t *);
int eccdev_ioctl(struct inode *, struct file *, unsigned int, unsigned long); long eccdev_ioctl(struct file *, unsigned int, unsigned long);
/*****************************************************************************/ /*****************************************************************************/
static struct file_operations eccdev_fops = { static struct file_operations eccdev_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = eccdev_open, .open = eccdev_open,
.release = eccdev_release, .release = eccdev_release,
.ioctl = eccdev_ioctl .unlocked_ioctl = eccdev_ioctl
}; };
/** \endcond */ /** \endcond */
...@@ -107,9 +107,11 @@ void ec_cdev_clear(ec_cdev_t *cdev /**< EtherCAT XML device */) ...@@ -107,9 +107,11 @@ void ec_cdev_clear(ec_cdev_t *cdev /**< EtherCAT XML device */)
int eccdev_open(struct inode *inode, struct file *filp) int eccdev_open(struct inode *inode, struct file *filp)
{ {
ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev); ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
ec_master_t *master = cdev->master;
filp->private_data = cdev; filp->private_data = cdev;
EC_DBG("File opened.\n"); if (master->debug_level)
EC_DBG("File opened.\n");
return 0; return 0;
} }
...@@ -117,88 +119,237 @@ int eccdev_open(struct inode *inode, struct file *filp) ...@@ -117,88 +119,237 @@ int eccdev_open(struct inode *inode, struct file *filp)
int eccdev_release(struct inode *inode, struct file *filp) int eccdev_release(struct inode *inode, struct file *filp)
{ {
//ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev); ec_cdev_t *cdev = (ec_cdev_t *) filp->private_data;
ec_master_t *master = cdev->master;
EC_DBG("File closed.\n"); if (master->debug_level)
EC_DBG("File closed.\n");
return 0; return 0;
} }
/*****************************************************************************/ /*****************************************************************************/
int eccdev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
unsigned long arg)
{ {
ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev); ec_cdev_t *cdev = (ec_cdev_t *) filp->private_data;
ec_master_t *master = cdev->master; ec_master_t *master = cdev->master;
long retval = 0;
if (master->debug_level) if (master->debug_level)
EC_DBG("ioctl(inode = %x, filp = %x, cmd = %u, arg = %u)\n", EC_DBG("ioctl(filp = %x, cmd = %u, arg = %u)\n",
(u32) inode, (u32) filp, (u32) cmd, (u32) arg); (u32) filp, (u32) cmd, (u32) arg);
// FIXME lock
switch (cmd) { switch (cmd) {
case EC_IOCTL_SLAVE_COUNT: case EC_IOCTL_SLAVE_COUNT:
retval = master->slave_count;
break;
case EC_IOCTL_SLAVE:
{ {
unsigned int slave_count = master->slave_count; ec_ioctl_slave_t data;
EC_INFO("EC_IOCTL_SLAVE_COUNT\n"); const ec_slave_t *slave;
if (!arg)
return -EFAULT; if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
if (copy_to_user((void __user *) arg, &slave_count, retval = -EFAULT;
sizeof(unsigned int))) break;
return -EFAULT; }
return 0;
if (!(slave = ec_master_find_slave(
master, 0, data.position))) {
EC_ERR("Slave %u does not exist!\n", data.position);
retval = -EINVAL;
break;
}
data.vendor_id = slave->sii.vendor_id;
data.product_code = slave->sii.product_code;
data.alias = slave->sii.alias;
data.state = slave->current_state;
data.sync_count = slave->sii.sync_count;
if (slave->sii.name) {
strncpy(data.name, slave->sii.name,
EC_IOCTL_SLAVE_NAME_SIZE);
data.name[EC_IOCTL_SLAVE_NAME_SIZE - 1] = 0;
} else {
data.name[0] = 0;
}
if (copy_to_user((void __user *) arg, &data, sizeof(data))) {
retval = -EFAULT;
break;
}
break;
} }
case EC_IOCTL_SLAVE_INFO: case EC_IOCTL_SYNC:
{ {
struct ec_ioctl_slave_info *infos, *info; ec_ioctl_sync_t data;
unsigned int slave_count = master->slave_count;
const ec_slave_t *slave; const ec_slave_t *slave;
unsigned int i = 0; const ec_sync_t *sync;
if (master->debug_level) if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
EC_DBG("EC_IOCTL_SLAVE_INFOS\n"); retval = -EFAULT;
break;
if (!slave_count) }
return 0;
if (!(slave = ec_master_find_slave(
if (!arg) master, 0, data.slave_position))) {
return -EFAULT; EC_ERR("Slave %u does not exist!\n", data.slave_position);
retval = -EINVAL;
if (!(infos = kmalloc(slave_count * break;
sizeof(struct ec_ioctl_slave_info), }
GFP_KERNEL)))
return -ENOMEM; if (data.sync_index >= slave->sii.sync_count) {
EC_ERR("Sync manager %u does not exist in slave %u!\n",
list_for_each_entry(slave, &master->slaves, list) { data.sync_index, data.slave_position);
info = &infos[i++]; retval = -EINVAL;
info->vendor_id = slave->sii.vendor_id; break;
info->product_code = slave->sii.product_code; }
info->alias = slave->sii.alias;
info->ring_position = slave->ring_position; sync = &slave->sii.syncs[data.sync_index];
info->state = slave->current_state;
if (slave->sii.name) { data.physical_start_address = sync->physical_start_address;
strncpy(info->description, slave->sii.name, data.default_size = sync->length;
EC_IOCTL_SLAVE_INFO_DESC_SIZE); data.control_register = sync->control_register;
info->description[EC_IOCTL_SLAVE_INFO_DESC_SIZE - 1] data.enable = sync->enable;
= 0; data.assign_source = sync->assign_source;
} else { data.pdo_count = ec_pdo_list_count(&sync->pdos);
info->description[0] = 0;
} if (copy_to_user((void __user *) arg, &data, sizeof(data))) {
} retval = -EFAULT;
break;
if (copy_to_user((void __user *) arg, infos, slave_count * }
sizeof(struct ec_ioctl_slave_info))) { break;
kfree(infos); }
return -EFAULT;
} case EC_IOCTL_PDO:
{
kfree(infos); ec_ioctl_pdo_t data;
return 0; const ec_slave_t *slave;
const ec_sync_t *sync;
const ec_pdo_t *pdo;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
retval = -EFAULT;
break;
}
if (!(slave = ec_master_find_slave(
master, 0, data.slave_position))) {
EC_ERR("Slave %u does not exist!\n", data.slave_position);
retval = -EINVAL;
break;
}
if (data.sync_index >= slave->sii.sync_count) {
EC_ERR("Sync manager %u does not exist in slave %u!\n",
data.sync_index, data.slave_position);
retval = -EINVAL;
break;
}
sync = &slave->sii.syncs[data.sync_index];
if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
&sync->pdos, data.pdo_pos))) {
EC_ERR("Sync manager %u does not contain a Pdo with "
"position %u in slave %u!\n", data.sync_index,
data.pdo_pos, data.slave_position);
retval = -EINVAL;
break;
}
data.dir = pdo->dir;
data.index = pdo->index;
data.entry_count = ec_pdo_entry_count(pdo);
if (pdo->name) {
strncpy(data.name, pdo->name, EC_IOCTL_PDO_NAME_SIZE);
data.name[EC_IOCTL_PDO_NAME_SIZE - 1] = 0;
} else {
data.name[0] = 0;
}
if (copy_to_user((void __user *) arg, &data, sizeof(data))) {
retval = -EFAULT;
break;
}
break;
}
case EC_IOCTL_PDO_ENTRY:
{
ec_ioctl_pdo_entry_t data;
const ec_slave_t *slave;
const ec_sync_t *sync;
const ec_pdo_t *pdo;
const ec_pdo_entry_t *entry;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
retval = -EFAULT;
break;
}
if (!(slave = ec_master_find_slave(
master, 0, data.slave_position))) {
EC_ERR("Slave %u does not exist!\n", data.slave_position);
retval = -EINVAL;
break;
}
if (data.sync_index >= slave->sii.sync_count) {
EC_ERR("Sync manager %u does not exist in slave %u!\n",
data.sync_index, data.slave_position);
retval = -EINVAL;
break;
}
sync = &slave->sii.syncs[data.sync_index];
if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
&sync->pdos, data.pdo_pos))) {
EC_ERR("Sync manager %u does not contain a Pdo with "
"position %u in slave %u!\n", data.sync_index,
data.pdo_pos, data.slave_position);
retval = -EINVAL;
break;
}
if (!(entry = ec_pdo_find_entry_by_pos_const(
pdo, data.entry_pos))) {
EC_ERR("Pdo 0x%04X does not contain an entry with "
"position %u in slave %u!\n", data.pdo_pos,
data.entry_pos, data.slave_position);
retval = -EINVAL;
break;
}
data.index = entry->index;
data.subindex = entry->subindex;
data.bit_length = entry->bit_length;
if (entry->name) {
strncpy(data.name, entry->name,
EC_IOCTL_PDO_ENTRY_NAME_SIZE);
data.name[EC_IOCTL_PDO_ENTRY_NAME_SIZE - 1] = 0;
} else {
data.name[0] = 0;
}
if (copy_to_user((void __user *) arg, &data, sizeof(data))) {
retval = -EFAULT;
break;
}
break;
} }
default: default:
return -ENOIOCTLCMD; retval = -ENOIOCTLCMD;
} }
return retval;
} }
/*****************************************************************************/ /*****************************************************************************/
...@@ -44,22 +44,80 @@ ...@@ -44,22 +44,80 @@
/*****************************************************************************/ /*****************************************************************************/
enum { enum {
EC_IOCTL_SLAVE_COUNT = 0, EC_IOCTL_SLAVE_COUNT,
EC_IOCTL_SLAVE_INFO, EC_IOCTL_SLAVE,
EC_IOCTL_SYNC,
EC_IOCTL_PDO,
EC_IOCTL_PDO_ENTRY,
}; };
/*****************************************************************************/ /*****************************************************************************/
#define EC_IOCTL_SLAVE_INFO_DESC_SIZE 243 #define EC_IOCTL_SLAVE_NAME_SIZE 114
struct ec_ioctl_slave_info { typedef struct {
// input
uint16_t position;
// outputs
uint32_t vendor_id; uint32_t vendor_id;
uint32_t product_code; uint32_t product_code;
uint16_t alias; uint16_t alias;
uint16_t ring_position;
uint8_t state; uint8_t state;
char description[EC_IOCTL_SLAVE_INFO_DESC_SIZE]; uint8_t sync_count;
}; char name[EC_IOCTL_SLAVE_NAME_SIZE];
} ec_ioctl_slave_t;
/*****************************************************************************/
typedef struct {
// inputs
uint16_t slave_position;
unsigned int sync_index;
// outputs
uint16_t physical_start_address;
uint16_t default_size;
uint8_t control_register;
uint8_t enable;
uint8_t assign_source;
uint8_t pdo_count;
} ec_ioctl_sync_t;
/*****************************************************************************/
#define EC_IOCTL_PDO_NAME_SIZE 114
typedef struct {
// inputs
uint16_t slave_position;
unsigned int sync_index;
unsigned int pdo_pos;
// outputs
uint8_t dir;
uint16_t index;
uint8_t entry_count;
char name[EC_IOCTL_PDO_NAME_SIZE];
} ec_ioctl_pdo_t;
/*****************************************************************************/
#define EC_IOCTL_PDO_ENTRY_NAME_SIZE 110
typedef struct {
// inputs
uint16_t slave_position;
unsigned int sync_index;
unsigned int pdo_pos;
unsigned int entry_pos;
// outputs
uint16_t index;
uint8_t subindex;
uint8_t bit_length;
char name[EC_IOCTL_PDO_NAME_SIZE];
} ec_ioctl_pdo_entry_t;
/*****************************************************************************/ /*****************************************************************************/
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
using namespace std; using namespace std;
#include "Master.h" #include "Master.h"
#include "../master/ioctl.h"
/****************************************************************************/ /****************************************************************************/
...@@ -63,70 +62,205 @@ void Master::close() ...@@ -63,70 +62,205 @@ void Master::close()
/****************************************************************************/ /****************************************************************************/
void Master::listSlaves()
{
unsigned int numSlaves = slaveCount(), i;
ec_ioctl_slave_t slave;
uint16_t lastAlias, aliasIndex;
lastAlias = 0;
aliasIndex = 0;
for (i = 0; i < numSlaves; i++) {
getSlave(&slave, i);
cout << setw(2) << i << " ";
if (slave.alias) {
lastAlias = slave.alias;
aliasIndex = 0;
}
if (lastAlias) {
cout << setw(10) << "#" << lastAlias << ":" << aliasIndex;
}
cout << " " << slaveState(slave.state) << " ";
if (strlen(slave.name)) {
cout << slave.name;
} else {
cout << "0x" << hex << setfill('0') << slave.vendor_id
<< ":0x" << slave.product_code;
}
cout << endl;
}
}
/****************************************************************************/
void Master::listPdos(int slavePosition)
{
ec_ioctl_slave_t slave;
ec_ioctl_sync_t sync;
ec_ioctl_pdo_t pdo;
ec_ioctl_pdo_entry_t entry;
unsigned int i, j, k;
getSlave(&slave, slavePosition);
for (i = 0; i < slave.sync_count; i++) {
getSync(&sync, slavePosition, i);
cout << "SM" << i << ":"
<< " PhysAddr 0x"
<< hex << setfill('0') << setw(4) << sync.physical_start_address
<< ", DefaultSize "
<< dec << setfill(' ') << setw(4) << sync.default_size
<< ", ControlRegister 0x"
<< hex << setfill('0') << setw(2)
<< (unsigned int) sync.control_register
<< ", Enable " << dec << (unsigned int) sync.enable
<< endl;
for (j = 0; j < sync.pdo_count; j++) {
getPdo(&pdo, slavePosition, i, j);
cout << " " << (pdo.dir ? "T" : "R") << "xPdo 0x"
<< hex << setfill('0') << setw(4) << pdo.index
<< " \"" << pdo.name << "\"" << endl;
for (k = 0; k < pdo.entry_count; k++) {
getPdoEntry(&entry, slavePosition, i, j, k);
cout << " Pdo entry 0x"
<< hex << setfill('0') << setw(4) << entry.index
<< ":" << hex << setfill('0') << setw(2)
<< (unsigned int) entry.subindex
<< ", " << dec << (unsigned int) entry.bit_length
<< " bit, \"" << entry.name << "\"" << endl;
}
}
}
}
/****************************************************************************/
unsigned int Master::slaveCount() unsigned int Master::slaveCount()
{ {
unsigned int numSlaves; int ret;
if (ioctl(fd, EC_IOCTL_SLAVE_COUNT, &numSlaves)) { if ((ret = ioctl(fd, EC_IOCTL_SLAVE_COUNT, 0)) < 0) {
stringstream err; stringstream err;
err << "Failed to get number of slaves: " << strerror(errno); err << "Failed to get slave: " << strerror(errno);
throw MasterException(err.str()); throw MasterException(err.str());
} }
return numSlaves; return ret;
} }
/****************************************************************************/ /****************************************************************************/
void Master::listSlaves() void Master::getSlave(ec_ioctl_slave_t *slave, uint16_t slaveIndex)
{ {
unsigned int numSlaves = slaveCount(), i; slave->position = slaveIndex;
struct ec_ioctl_slave_info *infos, *info;
uint16_t lastAlias, aliasIndex;
if (!numSlaves)
return;
infos = new struct ec_ioctl_slave_info[numSlaves];
if (ioctl(fd, EC_IOCTL_SLAVE_INFO, infos)) { if (ioctl(fd, EC_IOCTL_SLAVE, slave)) {
stringstream err; stringstream err;
err << "Failed to get slave information: " << strerror(errno); err << "Failed to get slave: ";
if (errno == EINVAL)
err << "Slave " << slaveIndex << " does not exist!";
else
err << strerror(errno);
throw MasterException(err.str()); throw MasterException(err.str());
} }
}
lastAlias = 0; /****************************************************************************/
aliasIndex = 0;
for (i = 0; i < numSlaves; i++) {
info = &infos[i];
cout << setw(2) << info->ring_position << " ";
if (info->alias) { void Master::getSync(
lastAlias = info->alias; ec_ioctl_sync_t *sync,
aliasIndex = 0; uint16_t slaveIndex,
} uint8_t syncIndex
if (lastAlias) { )
cout << setw(10) << "#" << lastAlias << ":" << aliasIndex; {
} sync->slave_position = slaveIndex;
sync->sync_index = syncIndex;
if (ioctl(fd, EC_IOCTL_SYNC, sync)) {
stringstream err;
err << "Failed to get sync manager: ";
if (errno == EINVAL)
err << "Either slave " << slaveIndex << " does not exist, "
<< "or contains less than " << (unsigned int) syncIndex + 1
<< " sync managers!";
else
err << strerror(errno);
throw MasterException(err.str());
}
}
cout << " " << slaveState(info->state) << " "; /****************************************************************************/
if (strlen(info->description)) { void Master::getPdo(
cout << info->description; ec_ioctl_pdo_t *pdo,
} else { uint16_t slaveIndex,
cout << "0x" << hex << setfill('0') << info->vendor_id uint8_t syncIndex,
<< ":0x" << info->product_code; uint8_t pdoPos
} )
{
pdo->slave_position = slaveIndex;
pdo->sync_index = syncIndex;
pdo->pdo_pos = pdoPos;
cout << endl; if (ioctl(fd, EC_IOCTL_PDO, pdo)) {
stringstream err;
err << "Failed to get Pdo: ";
if (errno == EINVAL)
err << "Either slave " << slaveIndex << " does not exist, "
<< "or contains less than " << (unsigned int) syncIndex + 1
<< " sync managers, or sync manager "
<< (unsigned int) syncIndex << " contains less than "
<< pdoPos + 1 << " Pdos!" << endl;
else
err << strerror(errno);
throw MasterException(err.str());
} }
}
delete [] infos; /****************************************************************************/
void Master::getPdoEntry(
ec_ioctl_pdo_entry_t *entry,
uint16_t slaveIndex,
uint8_t syncIndex,
uint8_t pdoPos,
uint8_t entryPos
)
{
entry->slave_position = slaveIndex;
entry->sync_index = syncIndex;
entry->pdo_pos = pdoPos;
entry->entry_pos = entryPos;
if (ioctl(fd, EC_IOCTL_PDO_ENTRY, entry)) {
stringstream err;
err << "Failed to get Pdo entry: ";
if (errno == EINVAL)
err << "Either slave " << slaveIndex << " does not exist, "
<< "or contains less than " << (unsigned int) syncIndex + 1
<< " sync managers, or sync manager "
<< (unsigned int) syncIndex << " contains less than "
<< pdoPos + 1 << " Pdos, or the Pdo at position " << pdoPos
<< " contains less than " << (unsigned int) entryPos + 1
<< " entries!" << endl;
else
err << strerror(errno);
throw MasterException(err.str());
}
} }
/****************************************************************************/ /****************************************************************************/
string Master::slaveState(uint8_t state) const string Master::slaveState(uint8_t state)
{ {
switch (state) { switch (state) {
case 1: return "INIT"; case 1: return "INIT";
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <stdexcept> #include <stdexcept>
using namespace std; using namespace std;
#include "../master/ioctl.h"
/****************************************************************************/ /****************************************************************************/
class MasterException: class MasterException:
...@@ -38,11 +40,19 @@ class Master ...@@ -38,11 +40,19 @@ class Master
void open(unsigned int); void open(unsigned int);
void close(); void close();
unsigned int slaveCount();
void listSlaves(); void listSlaves();
void listPdos(int);
protected: protected:
string slaveState(uint8_t) const; unsigned int slaveCount();
void slaveSyncs(uint16_t);
void getSlave(ec_ioctl_slave_t *, uint16_t);
void getSync(ec_ioctl_sync_t *, uint16_t, uint8_t);
void getPdo(ec_ioctl_pdo_t *, uint16_t, uint8_t, uint8_t);
void getPdoEntry(ec_ioctl_pdo_entry_t *, uint16_t, uint8_t, uint8_t,
uint8_t);
static string slaveState(uint8_t);
private: private:
unsigned int index; unsigned int index;
......
...@@ -19,7 +19,7 @@ using namespace std; ...@@ -19,7 +19,7 @@ using namespace std;
#define DEFAULT_SLAVESPEC "" #define DEFAULT_SLAVESPEC ""
static unsigned int masterIndex = DEFAULT_MASTER; static unsigned int masterIndex = DEFAULT_MASTER;
static string slaveSpec = DEFAULT_SLAVESPEC; static int slavePosition = -1;
static string command = DEFAULT_COMMAND; static string command = DEFAULT_COMMAND;
/*****************************************************************************/ /*****************************************************************************/
...@@ -30,10 +30,11 @@ void printUsage() ...@@ -30,10 +30,11 @@ void printUsage()
<< "Usage: ethercat <COMMAND> [OPTIONS]" << endl << "Usage: ethercat <COMMAND> [OPTIONS]" << endl
<< "Commands:" << endl << "Commands:" << endl
<< " list (ls, slaves) List all slaves (former 'lsec')." << endl << " list (ls, slaves) List all slaves (former 'lsec')." << endl
<< " pdos List Pdo mapping of given slaves." << endl
<< "Global options:" << endl << "Global options:" << endl
<< " --master -m <master> Index of the master to use. Default: " << " --master -m <master> Index of the master to use. Default: "
<< DEFAULT_MASTER << endl << DEFAULT_MASTER << endl
<< " --slave -s <slave> Slave specification. Default: All " << " --slave -s <slave> Numerical ring position. Default: All "
"slaves." << endl "slaves." << endl
<< " --help -h Show this help." << endl; << " --help -h Show this help." << endl;
} }
...@@ -68,7 +69,14 @@ void getOptions(int argc, char **argv) ...@@ -68,7 +69,14 @@ void getOptions(int argc, char **argv)
break; break;
case 's': case 's':
slaveSpec = optarg; number = strtoul(optarg, &remainder, 0);
if (remainder == optarg || *remainder
|| number < 0 || number > 0xFFFF) {
cerr << "Invalid slave position " << optarg << "!" << endl;
printUsage();
exit(1);
}
slavePosition = number;
break; break;
case 'h': case 'h':
...@@ -101,13 +109,21 @@ int main(int argc, char **argv) ...@@ -101,13 +109,21 @@ int main(int argc, char **argv)
getOptions(argc, argv); getOptions(argc, argv);
master.open(masterIndex); try {
master.open(masterIndex);
if (command == "list" || command == "ls" || command == "slaves") {
master.listSlaves();
if (command == "list" || command == "ls" || command == "slaves") { } else if (command == "pdos") {
master.listSlaves(); master.listPdos(slavePosition);
} else { } else {
cerr << "Unknown command " << command << "!" << endl; cerr << "Unknown command " << command << "!" << endl;
printUsage(); printUsage();
exit(1);
}
} catch (MasterException &e) {
cerr << e.what() << endl;
exit(1); exit(1);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment