Instrument Neutral Distributed Interface INDI 2.2.5
deepskydad_fp.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2015 Jasem Mutlaq. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2 of the License, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18
19 The full GNU General Public License is included in this distribution in the
20 file called LICENSE.
21*******************************************************************************/
22
23#include "deepskydad_fp.h"
24
25#include "indicom.h"
27
28#include <cerrno>
29#include <cstring>
30#include <memory>
31#include <termios.h>
32#include <unistd.h>
33#include <inttypes.h>
34#include <sys/ioctl.h>
35
36// We declare an auto pointer to DeepSkyDadFP.
37static std::unique_ptr<DeepSkyDadFP> dsdFp(new DeepSkyDadFP());
38
39#define FLAT_CMD 40
40#define FLAT_RES 40
41#define FLAT_TIMEOUT 3
42
47
49{
51
52 // Heater mode
53 IUFillSwitch(&HeaterModeS[Off], "OFF", "Off", ISS_OFF);
54 IUFillSwitch(&HeaterModeS[On], "ON", "On", ISS_OFF);
55 IUFillSwitch(&HeaterModeS[OnIfFlapOpenOrLedActive], "ON2", "On if flap open/LED active", ISS_OFF);
56 IUFillSwitchVector(&HeaterModeSP, HeaterModeS, 3, getDeviceName(), "Heater mode", "Heater mode", MAIN_CONTROL_TAB, IP_RW,
58
59 // Status
60 IUFillText(&StatusT[0], "Cover", "Cover", nullptr);
61 IUFillText(&StatusT[1], "Light", "Light", nullptr);
62 IUFillText(&StatusT[2], "Motor", "Motor", nullptr);
63 IUFillText(&StatusT[3], "Heater", "Heater", nullptr);
64 IUFillTextVector(&StatusTP, StatusT, 4, getDeviceName(), "Status", "Status", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
65
66 // Firmware version
67 IUFillText(&FirmwareT[0], "Version", "Version", nullptr);
68 IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
69
72
73 LightIntensityNP[0].setMin(0);
74 LightIntensityNP[0].setMax(4096);
75 LightIntensityNP[0].setStep(1);
76
78
80
81 serialConnection = new Connection::Serial(this);
82 serialConnection->registerHandshake([&]()
83 {
84 return Handshake();
85 });
86 registerConnection(serialConnection);
88 return true;
89}
90
91void DeepSkyDadFP::ISGetProperties(const char *dev)
92{
94
95 // Get Light box properties
97}
98
100{
102
105
106 if (isConnected())
107 {
108 defineProperty(&HeaterModeSP);
109 defineProperty(&StatusTP);
110 defineProperty(&FirmwareTP);
111
112 getStartupData();
113 }
114 else
115 {
116 deleteProperty(HeaterModeSP.name);
117 deleteProperty(StatusTP.name);
118 deleteProperty(FirmwareTP.name);
119 }
120
121 return true;
122}
123
125{
126 return "Deep Sky Dad FP";
127}
128
129bool DeepSkyDadFP::Handshake()
130{
131 PortFD = serialConnection->getPortFD();
132 if (!ping())
133 {
134 LOG_ERROR("Device ping failed.");
135 return false;
136 }
137
140
141 return true;
142}
143
144bool DeepSkyDadFP::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
145{
146 if (LI::processNumber(dev, name, values, names, n))
147 return true;
148
149 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
150}
151
152bool DeepSkyDadFP::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
153{
154 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
155 {
156 if (LI::processText(dev, name, texts, names, n))
157 return true;
158 }
159
160 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
161}
162
163bool DeepSkyDadFP::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
164{
165 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
166 {
167 if (DI::processSwitch(dev, name, states, names, n))
168 return true;
169
170 if (LI::processSwitch(dev, name, states, names, n))
171 return true;
172
173 if (strcmp(HeaterModeSP.name, name) == 0)
174 {
175 int current_mode = IUFindOnSwitchIndex(&HeaterModeSP);
176
177 IUUpdateSwitch(&HeaterModeSP, states, names, n);
178
179 int target_mode = IUFindOnSwitchIndex(&HeaterModeSP);
180
181 if (current_mode == target_mode)
182 {
183 HeaterModeSP.s = IPS_OK;
184 IDSetSwitch(&HeaterModeSP, nullptr);
185 return true;
186 }
187
188 char cmd[FLAT_RES] = {0};
189 char response[FLAT_RES] = {0};
190
191 snprintf(cmd, FLAT_RES, "[SHTM%d]", target_mode);
192 bool rc = sendCommand(cmd, response);
193 if (!rc)
194 {
195 IUResetSwitch(&HeaterModeSP);
196 HeaterModeS[current_mode].s = ISS_ON;
197 HeaterModeSP.s = IPS_ALERT;
198 IDSetSwitch(&HeaterModeSP, nullptr);
199 return false;
200 }
201
202 HeaterModeSP.s = IPS_OK;
203 IDSetSwitch(&HeaterModeSP, nullptr);
204 return true;
205 }
206 }
207
208 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
209}
210
212{
213 LI::snoop(root);
214
216}
217
219{
221
222 IUSaveConfigSwitch(fp, &HeaterModeSP);
223
224 return LI::saveConfigItems(fp);
225}
226
227bool DeepSkyDadFP::ping()
228{
229 char response[FLAT_RES] = {0};
230
231 if (!sendCommand("[GPOS]", response))
232 return false;
233
234 return true;
235}
236
238{
239 if (!isConnected())
240 return;
241
242 getStatus();
243
245}
246
247bool DeepSkyDadFP::getStartupData()
248{
249 bool rc1 = getFirmwareVersion();
250 bool rc2 = getStatus();
251 bool rc3 = getBrightness();
252
253 return (rc1 && rc2 && rc3);
254}
255
257{
258 char response[FLAT_RES];
259 if (!sendCommand("[STRG270]", response) || !sendCommand("[SMOV]", response))
260 return IPS_ALERT;
261
262 if (strcmp(response, "(OK)") == 0)
263 {
264 // Set cover status to random value outside of range to force it to refresh
265 prevCoverStatus = 10;
266 return IPS_BUSY;
267 }
268 else
269 return IPS_ALERT;
270}
271
273{
274 char response[FLAT_RES];
275 if (!sendCommand("[STRG0]", response) || !sendCommand("[SMOV]", response))
276 return IPS_ALERT;
277
278 if (strcmp(response, "(OK)") == 0)
279 {
280 // Set cover status to random value outside of range to force it to refresh
281 prevCoverStatus = 10;
282 return IPS_BUSY;
283 }
284 else
285 return IPS_ALERT;
286}
287
289{
290 char command[FLAT_CMD];
291 char response[FLAT_RES];
292
293 if (enable)
294 strncpy(command, "[SLON1]", FLAT_CMD);
295 else
296 strncpy(command, "[SLON0]", FLAT_CMD);
297
298 if (!sendCommand(command, response))
299 return false;
300
301 if (strcmp(response, "(OK)") == 0)
302 return true;
303
304 return false;
305}
306
307bool DeepSkyDadFP::getStatus()
308{
309 char response[FLAT_RES];
310
311 int motorStatus;
312 int lightStatus;
313 int coverStatus;
314 int heaterTemperature;
315 int heaterMode;
316
317 if (!sendCommand("[GMOV]", response))
318 return false;
319 else
320 sscanf(response, "(%d)", &motorStatus);
321
322 if (!sendCommand("[GLON]", response))
323 return false;
324 else
325 sscanf(response, "(%d)", &lightStatus);
326
327 if(isFP2) {
328 if (!sendCommand("[GOPS]", response))
329 return false;
330 else
331 sscanf(response, "(%d)", &coverStatus);
332 } else {
333 if (!sendCommand("[GPOS]", response))
334 return false;
335 else
336 sscanf(response, "(%d)", &coverStatus);
337 }
338
339 if (!sendCommand("[GHTT]", response))
340 return false;
341 else
342 sscanf(response, "(%d)", &heaterTemperature);
343
344 if (!sendCommand("[GHTM]", response))
345 return false;
346 else
347 sscanf(response, "(%d)", &heaterMode);
348
349
350 bool statusUpdated = false;
351
352 if(isFP2) {
353 if (coverStatus != prevCoverStatus)
354 {
355 if(motorStatus == 1)
356 {
357 if(coverStatus == 0)
358 {
359 IUSaveText(&StatusT[0], "Closed");
360 }
361 else if(coverStatus == 1)
362 {
363 IUSaveText(&StatusT[0], "Open");
364 }
365 else
366 {
367 IUSaveText(&StatusT[0], "Not open/closed");
368 }
369 }
370 else
371 {
372 prevCoverStatus = coverStatus;
373
374 statusUpdated = true;
375
376 if(coverStatus == 1)
377 {
378 IUSaveText(&StatusT[0], "Open");
380 {
384 LOG_INFO("Cover open.");
386 }
387 }
388 else if(coverStatus == 0)
389 {
390 IUSaveText(&StatusT[0], "Closed");
392 {
396 LOG_INFO("Cover closed.");
398 }
399 }
400 else
401 {
402 IUSaveText(&StatusT[0], "Not open/closed");
403 }
404 }
405 }
406 } else {
407 if (coverStatus != prevCoverStatus)
408 {
409 if(motorStatus == 1)
410 {
411 if(coverStatus == 0)
412 {
413 IUSaveText(&StatusT[0], "Open");
414 }
415 else if(coverStatus == 270)
416 {
417 IUSaveText(&StatusT[0], "Closed");
418 }
419 else
420 {
421 IUSaveText(&StatusT[0], "Not open/closed");
422 }
423 }
424 else
425 {
426 prevCoverStatus = coverStatus;
427
428 statusUpdated = true;
429
430 if(coverStatus == 0)
431 {
432 IUSaveText(&StatusT[0], "Open");
434 {
438 LOG_INFO("Cover open.");
440 }
441 }
442 else if(coverStatus == 270)
443 {
444 IUSaveText(&StatusT[0], "Closed");
446 {
450 LOG_INFO("Cover closed.");
452 }
453 }
454 else
455 {
456 IUSaveText(&StatusT[0], "Not open/closed");
457 }
458 }
459 }
460 }
461
462 if(motorStatus == 1)
463 {
464 IUSaveText(&StatusT[0], "Moving");
465 }
466
467 if (lightStatus != prevLightStatus)
468 {
469 prevLightStatus = lightStatus;
470
471 statusUpdated = true;
472
473 switch (lightStatus)
474 {
475 case 0:
476 IUSaveText(&StatusT[1], "Off");
479 LightSP.apply();
480 break;
481
482 case 1:
483 IUSaveText(&StatusT[1], "On");
486 LightSP.apply();
487 break;
488 }
489 }
490
491 if (motorStatus != prevMotorStatus)
492 {
493 prevMotorStatus = motorStatus;
494
495 statusUpdated = true;
496
497 switch (motorStatus)
498 {
499 case 0:
500 IUSaveText(&StatusT[2], "Stopped");
501 break;
502
503 case 1:
504 IUSaveText(&StatusT[2], "Running");
505 break;
506 }
507 }
508
509 if (statusUpdated)
510 IDSetText(&StatusTP, nullptr);
511
512 int heaterConnected = heaterTemperature > -40;
513 if(heaterConnected != prevHeaterConnected)
514 {
515 prevHeaterConnected = heaterConnected;
516 if(heaterConnected == 1)
517 {
518 IUSaveText(&StatusT[3], "Connected");
519 HeaterModeSP.s = IPS_OK;
520 }
521 else
522 {
523 HeaterModeSP.s = IPS_IDLE;
524 IUSaveText(&StatusT[3], "Disconnected");
525 }
526 }
527
528 if(heaterMode != prevHeaterMode)
529 {
530 prevHeaterMode = heaterMode;
531 IUResetSwitch(&HeaterModeSP);
532 if(heaterMode == 0)
533 {
534 HeaterModeS[0].s = ISS_ON;
535 }
536 else if(heaterMode == 1)
537 {
538 HeaterModeS[1].s = ISS_ON;
539 }
540 else if(heaterMode == 2)
541 {
542 HeaterModeS[2].s = ISS_ON;
543 }
544 IDSetSwitch(&HeaterModeSP, nullptr);
545 }
546
547 return true;
548}
549
550bool DeepSkyDadFP::getFirmwareVersion()
551{
552 char response[FLAT_RES] = {0};
553 if (!sendCommand("[GFRM]", response))
554 return false;
555
556 char versionString[6] = { 0 };
557 snprintf(versionString, 6, "%s", response + 31);
558 IUSaveText(&FirmwareT[0], response);
559 IDSetText(&FirmwareTP, nullptr);
560 isFP2 = strstr(response, "DeepSkyDad.FP2") != nullptr;
561
562 return true;
563}
564
565bool DeepSkyDadFP::getBrightness()
566{
567 char response[FLAT_RES] = {0};
568 if (!sendCommand("[GLBR]", response))
569 return false;
570
571 int32_t brightnessValue;
572 int rc = sscanf(response, "(%d)", &brightnessValue);
573
574 if (rc <= 0)
575 {
576 LOGF_ERROR("Unable to parse brightness value (%s)", response);
577 return false;
578 }
579
580 if (brightnessValue != prevBrightness)
581 {
582 prevBrightness = brightnessValue;
583 LightIntensityNP[0].setValue(brightnessValue);
585 }
586
587 return true;
588}
589
591{
592 char command[FLAT_CMD] = {0};
593 char response[FLAT_RES] = {0};
594 snprintf(command, FLAT_CMD, "[SLBR%04d]", value);
595
596 if (!sendCommand(command, response))
597 return false;
598
599 if (strcmp(response, "(OK)") != 0)
600 {
601 LOGF_ERROR("Unable to set brightness value", response);
602 return false;
603 }
604
605 return true;
606}
607
608bool DeepSkyDadFP::sendCommand(const char *cmd, char *res)
609{
610 int nbytes_written = 0, nbytes_read = 0, rc = -1;
611
612 tcflush(PortFD, TCIOFLUSH);
613
614 LOGF_DEBUG("CMD <%s>", cmd);
615
616 if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
617 {
618 char errstr[MAXRBUF] = {0};
619 tty_error_msg(rc, errstr, MAXRBUF);
620 LOGF_ERROR("Serial write error: %s.", errstr);
621 return false;
622 }
623
624 if (res == nullptr)
625 return true;
626
627 if ((rc = tty_nread_section(PortFD, res, FLAT_RES, ')', FLAT_TIMEOUT, &nbytes_read)) != TTY_OK)
628 {
629 char errstr[MAXRBUF] = {0};
630 tty_error_msg(rc, errstr, MAXRBUF);
631 LOGF_ERROR("Serial read error: %s.", errstr);
632 return false;
633 }
634
635 LOGF_DEBUG("RES <%s>", res);
636
637 tcflush(PortFD, TCIOFLUSH);
638
639 return true;
640}
#define FLAT_TIMEOUT
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual IPState UnParkCap() override
unPark dust cap (open cover). Must be implemented by child.
virtual bool SetLightBoxBrightness(uint16_t value) override
setBrightness Set light level. Must be implemented in the child class, if supported.
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool EnableLightBox(bool enable) override
EnableLightBox Turn on/off on a light box. Must be implemented in the child class.
virtual IPState ParkCap() override
Park dust cap (close cover). Must be implemented by child.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
const char * getDefaultName() override
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Provides interface to implement remotely controlled dust cover.
bool isConnected() const
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
INDI::PropertySwitch ParkCapSP
bool updateProperties()
updateProperties Defines or Delete properties based on default device connection status
void initProperties(const char *group, uint32_t capabilities=0)
Initialize dust cap properties. It is recommended to call this function within initProperties() of yo...
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process dust cap switch properties.
void initProperties(const char *group, uint32_t capabilities)
Initialize light box properties. It is recommended to call this function within initProperties() of y...
void ISGetProperties(const char *deviceName)
isGetLightBoxProperties Get light box properties
INDI::PropertyNumber LightIntensityNP
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process light box switch properties.
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process light box text properties.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process light box number properties.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
Provides interface to implement controllable light box/switch device.
#define FLAT_CMD
#define FLAT_RES
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition indidevapi.c:416
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248