Tizen 2.1 base
[platform/upstream/hplip.git] / io / hpmud / hpmud.c
1 /*****************************************************************************\
2
3   hpmud.cpp - multi-point transport driver
4  
5   (c) 2004-2007 Copyright Hewlett-Packard Development Company, LP
6
7   Permission is hereby granted, free of charge, to any person obtaining a copy 
8   of this software and associated documentation files (the "Software"), to deal 
9   in the Software without restriction, including without limitation the rights 
10   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
11   of the Software, and to permit persons to whom the Software is furnished to do 
12   so, subject to the following conditions:
13
14   The above copyright notice and this permission notice shall be included in all
15   copies or substantial portions of the Software.
16
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
18   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 
19   FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
20   COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
21   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
22   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24   Author: Naga Samrat Chowdary Narla,
25   Contributor: Sarbeswar Meher
26 \*****************************************************************************/
27
28 #include "hpmud.h"
29 #include "hpmudi.h"
30
31 /* Client data. */
32 mud_session ms __attribute__ ((visibility ("hidden")));      /* mud session, one per client */
33 mud_session *msp __attribute__ ((visibility ("hidden"))) = &ms;
34
35 /*
36  * sysdump() originally came from http://sws.dett.de/mini/hexdump-c , steffen@dett.de .  
37  */
38 void __attribute__ ((visibility ("hidden"))) sysdump(const void *data, int size)
39 {
40     /* Dump size bytes of *data. Output looks like:
41      * [0000] 75 6E 6B 6E 6F 77 6E 20 30 FF 00 00 00 00 39 00 unknown 0.....9.
42      */
43
44     unsigned char *p = (unsigned char *)data;
45     unsigned char c;
46     int n;
47     char bytestr[4] = {0};
48     char addrstr[10] = {0};
49     char hexstr[16*3 + 5] = {0};
50     char charstr[16*1 + 5] = {0};
51     for(n=1;n<=size;n++) {
52         if (n%16 == 1) {
53             /* store address for this line */
54             snprintf(addrstr, sizeof(addrstr), "%.4d", (int)((p-(unsigned char *)data) & 0xffff));
55         }
56             
57         c = *p;
58         if (isprint(c) == 0) {
59             c = '.';
60         }
61
62         /* store hex str (for left side) */
63         snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
64         strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
65
66         /* store char str (for right side) */
67         snprintf(bytestr, sizeof(bytestr), "%c", c);
68         strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
69
70         if(n%16 == 0) { 
71             /* line completed */
72             DBG_SZ("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
73             hexstr[0] = 0;
74             charstr[0] = 0;
75         }
76         p++; /* next byte */
77     }
78
79     if (strlen(hexstr) > 0) {
80         /* print rest of buffer if not empty */
81         DBG_SZ("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
82     }
83 }
84
85 /* Given the IEEE 1284 device id string, determine if this is a HP product. */
86 int __attribute__ ((visibility ("hidden"))) is_hp(const char *id)
87 {
88    char *pMf;
89
90    if ((pMf = strstr(id, "MFG:")) != NULL)
91       pMf+=4;
92    else if ((pMf = strstr(id, "MANUFACTURER:")) != NULL)
93       pMf+=13;
94    else
95       return 0;
96
97    if ((strncasecmp(pMf, "HEWLETT-PACKARD", 15) == 0) ||
98       (strncasecmp(pMf, "APOLLO", 6) == 0) || (strncasecmp(pMf, "HP", 2) == 0))
99    {
100       return 1;  /* found HP product */
101    }
102    return 0;   
103 }
104
105 int __attribute__ ((visibility ("hidden"))) generalize_model(const char *sz, char *buf, int bufSize)
106 {
107    const char *pMd=sz;
108    int i, j, dd=0;
109
110    for (i=0; pMd[i] == ' ' && i < bufSize; i++);  /* eat leading white space */
111
112    for (j=0; (pMd[i] != 0) && (pMd[i] != ';') && (j < bufSize); i++)
113    {
114       if (pMd[i]==' ' || pMd[i]=='/')
115       {
116          /* Remove double spaces. */
117          if (!dd)
118          { 
119             buf[j++] = '_';   /* convert space to "_" */
120             dd=1;              
121          }
122       }
123       else
124       {
125          buf[j++] = pMd[i];
126          dd=0;       
127       }
128    }
129
130    for (j--; buf[j] == '_' && j > 0; j--);  /* eat trailing white space */
131
132    buf[++j] = 0;
133
134    return j;   /* length does not include zero termination */
135 }
136
137 int __attribute__ ((visibility ("hidden"))) generalize_serial(const char *sz, char *buf, int bufSize)
138 {
139    const char *pMd=sz;
140    int i, j;
141
142    for (i=0; pMd[i] == ' ' && i < bufSize; i++);  /* eat leading white space */
143
144    for (j=0; (pMd[i] != 0) && (i < bufSize); i++)
145    {
146       buf[j++] = pMd[i];
147    }
148
149    for (i--; buf[i] == ' ' && i > 0; i--);  /* eat trailing white space */
150
151    buf[++i] = 0;
152
153    return i;   /* length does not include zero termination */
154 }
155
156 /* Parse serial number from uri string. */
157 int __attribute__ ((visibility ("hidden"))) get_uri_serial(const char *uri, char *buf, int bufSize)
158 {
159    char *p;
160    int i;
161
162    buf[0] = 0;
163
164    if ((p = strcasestr(uri, "serial=")) != NULL)
165       p+=7;
166    else
167       return 0;
168
169    for (i=0; (p[i] != 0) && (p[i] != '+') && (i < bufSize); i++)
170       buf[i] = p[i];
171
172    buf[i] = 0;
173
174    return i;
175 }
176
177 enum HPMUD_RESULT __attribute__ ((visibility ("hidden"))) service_to_channel(mud_device *pd, const char *sn, HPMUD_CHANNEL *index)
178 {
179    enum HPMUD_RESULT stat;
180
181    *index=-1;
182    
183    /* Check for valid service requests. */
184    if (strncasecmp(sn, "print", 5) == 0)
185    {
186       *index = HPMUD_PRINT_CHANNEL;
187    }
188    else if (strncasecmp(sn, "hp-ews-ledm", 11) == 0)
189    {
190       *index = HPMUD_EWS_LEDM_CHANNEL;
191    }
192    else if (strncasecmp(sn, "hp-ews", 6) == 0)
193    {
194       *index = HPMUD_EWS_CHANNEL;
195    }
196    else if (strncasecmp(sn, "hp-soap-scan", 12) == 0)
197    {
198       *index = HPMUD_SOAPSCAN_CHANNEL;
199    }
200    else if (strncasecmp(sn, "hp-soap-fax", 11) == 0)
201    {
202       *index = HPMUD_SOAPFAX_CHANNEL;
203    }
204    else if (strncasecmp(sn, "hp-marvell-scan", 15) == 0)
205    {
206       *index = HPMUD_MARVELL_SCAN_CHANNEL;
207    }
208    else if (strncasecmp(sn, "hp-marvell-fax", 14) == 0)
209    {
210       *index = HPMUD_MARVELL_FAX_CHANNEL;
211    }
212    else if (strncasecmp(sn, "hp-ledm-scan", 12) == 0)
213    {
214       *index = HPMUD_LEDM_SCAN_CHANNEL;
215    }
216    /* All the following services require MLC/1284.4. */
217    else if (pd->io_mode == HPMUD_RAW_MODE || pd->io_mode == HPMUD_UNI_MODE)
218    {
219       BUG("invalid channel_open state, current io_mode=raw/uni service=%s %s\n", sn, pd->uri);
220       stat = HPMUD_R_INVALID_STATE;
221       goto bugout;
222    }
223    else if (strncasecmp(sn, "hp-message", 10) == 0)
224    {
225       *index = HPMUD_PML_CHANNEL;
226    }
227    else if (strncasecmp(sn, "hp-scan", 7) == 0)
228    {
229       *index = HPMUD_SCAN_CHANNEL;
230    }
231    else if (strncasecmp(sn, "hp-fax-send", 11) == 0)
232    {
233       *index = HPMUD_FAX_SEND_CHANNEL;
234    }
235    else if (strncasecmp(sn, "hp-card-access", 14) == 0)
236    {
237       *index = HPMUD_MEMORY_CARD_CHANNEL;
238    }
239    else if (strncasecmp(sn, "hp-configuration-upload", 23) == 0)
240    {
241       *index = HPMUD_CONFIG_UPLOAD_CHANNEL;
242    }
243    else if (strncasecmp(sn, "hp-configuration-download", 25) == 0)
244    {
245       *index = HPMUD_CONFIG_DOWNLOAD_CHANNEL;
246    }
247    else if (strncasecmp(sn, "hp-devmgmt", 10) == 0)
248    {
249       *index = HPMUD_DEVMGMT_CHANNEL;
250    }
251    else if (strncasecmp(sn, "hp-wificonfig", 13) == 0)
252    {
253       *index = HPMUD_WIFI_CHANNEL;
254    }
255    else
256    {
257       BUG("invalid service=%s %s\n", sn, pd->uri);
258       stat = HPMUD_R_INVALID_SN;
259       goto bugout;
260    }
261
262    stat = HPMUD_R_OK;
263
264 bugout:
265    return stat;
266 }
267
268 static int new_device(const char *uri, enum HPMUD_IO_MODE mode, int *result)
269 {
270    int index=0;      /* device[0] is unused */
271    int i=1;
272
273    if (uri[0] == 0)
274       return 0;
275    
276    pthread_mutex_lock(&msp->mutex);
277    
278    if (msp->device[i].index)
279    {
280       BUG("invalid device_open state\n");        /* device is already open for this client, one device per session */
281       *result = HPMUD_R_INVALID_STATE;
282       goto bugout;
283    }
284
285    index = i;      /* currently only support one device per client or process */
286
287    /* Based on uri, set local session attributes. */
288    if (strcasestr(uri, ":/usb") != NULL)
289    {
290       msp->device[i].vf = musb_mud_device_vf;
291    }
292 #ifdef HAVE_LIBNETSNMP
293    else if (strcasestr(uri, ":/net") != NULL)
294    {
295       msp->device[i].vf = jd_mud_device_vf;
296    }
297 #endif
298 #ifdef HAVE_PPORT
299    else if (strcasestr(uri, ":/par") != NULL)
300    {
301       msp->device[i].vf = pp_mud_device_vf;
302    }
303 #endif
304    else
305    {
306       BUG("invalid uri %s\n", uri);
307       *result = HPMUD_R_INVALID_URI;
308       index = 0;
309       goto bugout;
310    }
311    msp->device[i].io_mode = mode;
312    msp->device[i].index = index;
313    msp->device[i].channel_cnt = 0;
314    msp->device[i].open_fd = -1;
315    strcpy(msp->device[i].uri, uri);
316
317 bugout:
318    pthread_mutex_unlock(&msp->mutex);
319
320    return index;  /* return device index */
321 }
322
323 static int del_device(HPMUD_DEVICE index)
324 {
325    pthread_mutex_lock(&msp->mutex);
326
327    msp->device[index].index = 0;
328
329    pthread_mutex_unlock(&msp->mutex);
330
331    return 0;
332 }
333
334 /*  Make sure client closed down the device. */
335 int device_cleanup(mud_session *ps)
336 {
337    int i, dd=1;
338
339    if(!ps->device[dd].index)
340       return 0;          /* nothing to do */
341
342    BUG("device_cleanup: device uri=%s\n", ps->device[dd].uri);
343
344    for (i=0; i<HPMUD_CHANNEL_MAX; i++)
345    {
346       if (ps->device[dd].channel[i].client_cnt)
347       {
348          BUG("device_cleanup: close channel %d...\n", i);
349          hpmud_close_channel(dd, ps->device[dd].channel[i].index);
350          BUG("device_cleanup: done closing channel %d\n", i);
351       }
352    }
353
354    BUG("device_cleanup: close device dd=%d...\n", dd);
355    hpmud_close_device(dd);
356    BUG("device_cleanup: done closing device dd=%d\n", dd);
357
358    return 0;
359 }
360
361 static void __attribute__ ((constructor)) mud_init(void)
362 {
363    DBG("[%d] hpmud_init()\n", getpid());
364 }
365
366 static void __attribute__ ((destructor)) mud_exit(void)
367 {
368    DBG("[%d] hpmud_exit()\n", getpid());
369    device_cleanup(msp);
370 }
371
372 /*******************************************************************************************************************************
373  * Helper functions.
374  */
375
376 /* Parse the model from the IEEE 1284 device id string and generalize the model name */
377 int hpmud_get_model(const char *id, char *buf, int buf_size)
378 {
379    char *pMd;
380
381    buf[0] = 0;
382
383    if ((pMd = strstr(id, "MDL:")) != NULL)
384       pMd+=4;
385    else if ((pMd = strstr(id, "MODEL:")) != NULL)
386       pMd+=6;
387    else
388       return 0;
389
390    return generalize_model(pMd, buf, buf_size);
391 }
392
393 /* Parse the model from the IEEE 1284 device id string. */
394 int hpmud_get_raw_model(char *id, char *raw, int rawSize)
395 {
396    char *pMd;
397    int i;
398
399    raw[0] = 0;
400
401    if ((pMd = strstr(id, "MDL:")) != NULL)
402       pMd+=4;
403    else if ((pMd = strstr(id, "MODEL:")) != NULL)
404       pMd+=6;
405    else
406       return 0;
407
408    for (i=0; (pMd[i] != ';') && (i < rawSize); i++)
409       raw[i] = pMd[i];
410    raw[i] = 0;
411
412    return i;
413 }
414
415 /* Parse device model from uri string. */
416 int hpmud_get_uri_model(const char *uri, char *buf, int buf_size)
417 {
418    char *p;
419    int i;
420
421    buf[0] = 0;
422
423    if ((p = strstr(uri, "/")) == NULL)
424       return 0;
425    if ((p = strstr(p+1, "/")) == NULL)
426       return 0;
427    p++;
428
429    for (i=0; (p[i] != '?') && (i < buf_size); i++)
430       buf[i] = p[i];
431
432    buf[i] = 0;
433
434    return i;
435 }
436
437 /* Parse the data link from a uri string. */
438 int hpmud_get_uri_datalink(const char *uri, char *buf, int buf_size)
439 {
440    char *p;
441    int i;
442    int zc=0;
443 #ifdef HAVE_LIBNETSNMP
444    char ip[HPMUD_LINE_SIZE];
445 #endif
446
447    buf[0] = 0;
448
449    if ((p = strcasestr(uri, "device=")) != NULL)
450       p+=7;
451    else if ((p = strcasestr(uri, "ip=")) != NULL)
452       p+=3;
453    else if ((p = strcasestr(uri, "zc=")) != NULL)
454    {
455       p+=3;
456       zc=1;
457    }
458    else
459       return 0;
460
461    if (zc)
462    {
463 #ifdef HAVE_LIBNETSNMP
464       if (hpmud_mdns_lookup(p, HPMUD_MDNS_TIMEOUT, ip) != HPMUD_R_OK)
465          return 0;
466       for (i=0; (ip[i] != 0) && (i < buf_size); i++)
467          buf[i] = ip[i];
468 #else
469       return 0;
470 #endif
471    }
472    else {
473       for (i=0; (p[i] != 0) && (p[i] != '&') && (i < buf_size); i++)
474          buf[i] = p[i];
475    }
476
477    buf[i] = 0;
478
479    return i;
480 }
481
482 /***************************************************************************************************
483  * Core functions.
484  */
485
486 enum HPMUD_RESULT hpmud_open_device(const char *uri, enum HPMUD_IO_MODE iomode, HPMUD_DEVICE *dd)
487 {
488    HPMUD_DEVICE index=0;
489    enum HPMUD_RESULT stat = HPMUD_R_INVALID_URI;
490    int result;
491
492    DBG("[%d,%d,%d,%d,%d,%d] hpmud_device_open() uri=%s iomode=%d\n", getpid(), getppid(), getuid(), geteuid(), getgid(), getegid(), uri, iomode);
493
494    if ((index = new_device(uri, iomode, &result)) == 0)
495    {   
496       stat = result;
497       goto bugout;
498    }
499    else
500    {
501       if ((stat = (msp->device[index].vf.open)(&msp->device[index])) != HPMUD_R_OK)
502       {
503          (msp->device[index].vf.close)(&msp->device[index]);  /* Open failed perform device cleanup. */
504          del_device(index);
505          goto bugout;
506       }
507    }
508
509    *dd = index;
510    stat = HPMUD_R_OK;
511
512 bugout:
513    return stat;
514 }
515
516 enum HPMUD_RESULT hpmud_close_device(HPMUD_DEVICE dd)
517 {
518    enum HPMUD_RESULT stat;
519
520    DBG("[%d] hpmud_device_close() dd=%d\n", getpid(), dd);
521
522    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd)
523    {
524       BUG("invalid device_close state\n");
525       stat = HPMUD_R_INVALID_STATE;
526    }
527    else
528    {
529       stat = (msp->device[dd].vf.close)(&msp->device[dd]);
530       del_device(dd);
531    }
532    return stat;
533 }
534
535 enum HPMUD_RESULT hpmud_get_device_id(HPMUD_DEVICE dd, char *buf, int size, int *bytes_read)
536 {
537    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
538
539    DBG("[%d] hpmud_get_device_id() dd=%d\n", getpid(), dd);
540
541    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd)
542    {
543       BUG("invalid get_device_id state\n");
544       goto bugout;
545    }
546
547    stat = (msp->device[dd].vf.get_device_id)(&msp->device[dd], buf, size, bytes_read);
548
549 bugout:
550    return stat;
551 }
552
553 enum HPMUD_RESULT hpmud_get_device_status(HPMUD_DEVICE dd, unsigned int *status)
554 {
555    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
556
557    DBG("[%d] hpmud_get_device_status() dd=%d\n", getpid(), dd);
558
559    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd)
560    {
561       BUG("invalid get_device_status state\n");
562       goto bugout;
563    }
564
565    stat = (msp->device[dd].vf.get_device_status)(&msp->device[dd], status);
566
567 bugout:
568    return stat;
569 }
570
571 enum HPMUD_RESULT hpmud_probe_devices(enum HPMUD_BUS_ID bus, char *buf, int buf_size, int *cnt, int *bytes_read)
572 {
573    int len=0;
574
575    DBG("[%d] hpmud_probe_devices() bus=%d\n", getpid(), bus);
576
577    buf[0] = 0;
578    *cnt = 0;
579
580    if (bus == HPMUD_BUS_USB)
581    {
582       len = musb_probe_devices(buf, buf_size, cnt);
583    }
584 #ifdef HAVE_PPORT
585    else if (bus == HPMUD_BUS_PARALLEL)
586    {
587       len = pp_probe_devices(buf, buf_size, cnt);
588    }
589 #endif
590    else if (bus == HPMUD_BUS_ALL)
591    {
592       len = musb_probe_devices(buf, buf_size, cnt);
593 #ifdef HAVE_PPORT
594       len += pp_probe_devices(buf+len, buf_size-len, cnt);
595 #endif
596    }
597
598    *bytes_read = len;
599
600    return HPMUD_R_OK;
601 }
602
603 enum HPMUD_RESULT hpmud_open_channel(HPMUD_DEVICE dd, const char *channel_name, HPMUD_CHANNEL *cd)
604 {
605    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
606
607    DBG("[%d] hpmud_channel_open() dd=%d name=%s\n", getpid(), dd, channel_name);
608
609    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd)
610    {
611       BUG("invalid channel_open state\n");
612       goto bugout;
613    }
614
615    stat = (msp->device[dd].vf.channel_open)(&msp->device[dd], channel_name, cd);
616
617 bugout:
618    return stat;
619 }
620
621 enum HPMUD_RESULT hpmud_close_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd)
622 {
623    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
624
625    DBG("[%d] hpmud_channel_close() dd=%d cd=%d\n", getpid(), dd, cd);
626
627    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd ||
628         cd <=0 || cd > HPMUD_CHANNEL_MAX || msp->device[dd].channel[cd].client_cnt == 0)
629    {
630       BUG("invalid channel_close state\n");
631       goto bugout;
632    }
633
634    stat = (msp->device[dd].vf.channel_close)(&msp->device[dd], &msp->device[dd].channel[cd]);
635
636 bugout:
637    return stat;
638 }
639
640 enum HPMUD_RESULT hpmud_write_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, const void *buf, int size, int sec_timeout, int *bytes_wrote)
641 {
642    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
643
644    DBG("[%d] hpmud_channel_write() dd=%d cd=%d buf=%p size=%d sectime=%d\n", getpid(), dd, cd, buf, size, sec_timeout);
645
646    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd ||
647         cd <=0 || cd > HPMUD_CHANNEL_MAX || msp->device[dd].channel[cd].client_cnt == 0)
648    {
649       BUG("invalid channel_write state\n");
650       goto bugout;
651    }
652
653    stat = (msp->device[dd].vf.channel_write)(&msp->device[dd], &msp->device[dd].channel[cd], buf, size, sec_timeout, bytes_wrote);
654
655 bugout:
656    return stat;
657 }
658
659 enum HPMUD_RESULT hpmud_read_channel(HPMUD_DEVICE dd, HPMUD_CHANNEL cd, void *buf, int size, int sec_timeout, int *bytes_read)
660 {
661    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
662    DBG("[%d] hpmud_channel_read() dd=%d cd=%d buf=%p size=%d sectime=%d\n", getpid(), dd, cd, buf, size, sec_timeout);
663
664    if (dd <= 0 || dd > HPMUD_DEVICE_MAX || msp->device[dd].index != dd ||
665         cd <=0 || cd > HPMUD_CHANNEL_MAX || msp->device[dd].channel[cd].client_cnt == 0)
666    {
667       BUG("invalid channel_read state\n");
668       goto bugout;
669    }
670
671    stat = (msp->device[dd].vf.channel_read)(&msp->device[dd], &msp->device[dd].channel[cd], buf, size, sec_timeout, bytes_read);
672
673 bugout:
674    return stat;
675 }
676
677 enum HPMUD_RESULT hpmud_get_dstat(HPMUD_DEVICE dd, struct hpmud_dstat *ds)
678 {
679    enum HPMUD_RESULT stat = HPMUD_R_INVALID_STATE;
680
681    DBG("[%d] hpmud_dstat() dd=%d ds=%p\n", getpid(), dd, ds);
682
683    if (dd <= 0 || dd > HPMUD_DEVICE_MAX)
684    {
685       BUG("invalid dstat state\n");
686       goto bugout;
687    }
688
689    strncpy(ds->uri, msp->device[dd].uri, sizeof(ds->uri));
690    ds->io_mode = msp->device[dd].io_mode;
691    ds->channel_cnt = msp->device[dd].channel_cnt;
692    ds->mlc_up = msp->device[dd].mlc_up;
693
694    stat = HPMUD_R_OK;
695
696 bugout:
697    return stat;
698 }
699