Tizen 2.1 base
[platform/upstream/hplip.git] / prnt / hpijs / registry.cpp
1 /*****************************************************************************\
2   registry.cpp : Implimentation for the DeviceRegistry class
3
4   Copyright (c) 1996 - 2001, Hewlett-Packard Co.
5   All rights reserved.
6
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10   1. Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15   3. Neither the name of Hewlett-Packard nor the names of its
16      contributors may be used to endorse or promote products derived
17      from this software without specific prior written permission.
18
19   THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
22   NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24   TO, PATENT INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25   OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 \*****************************************************************************/
30
31
32 // The purpose of this file is to facilitate addition and subtraction
33 // of supported devices from the system.
34
35 #include "header.h"
36 #include "printerfactory.h"
37
38 #include "printer.h"
39 #include "apollo2xxx.h"
40 #include "apollo21xx.h"
41 #include "apollo2560.h"
42 #include "apollo2xxx.h"
43 #ifdef APDK_DJ3320
44 #include "dj3320.h"
45 #ifdef APDK_DJ3600
46 #include "dj3600.h"
47 #include "dj4100.h"
48 #include "djd2600.h"
49 #endif
50 #endif
51 #include "dj400.h"
52 #include "dj6xx.h"
53 #include "dj600.h"
54 #include "dj630.h"
55 #include "dj660.h"
56 #include "dj690.h"
57 #include "dj350.h"
58 #include "dj540.h"
59 #include "dj8xx.h"
60 #include "dj850.h"
61 #include "dj8x5.h"
62 #include "dj890.h"
63 #include "dj9xx.h"
64 #include "dj9xxvip.h"
65 #include "djgenericvip.h"
66 #include "dj55xx.h"
67 #include "ojprokx50.h"
68 #include "ljmono.h"
69 #include "ljcolor.h"
70 #include "psp100.h"
71 #include "psp470.h"
72 #include "pscript.h"
73 #include "ljjetready.h"
74 #include "ljfastraster.h"
75 #if defined (APDK_LJZJS_MONO) || defined (APDK_LJZJS_COLOR) || defined (APDK_LJM1005)
76 #include "ljzjs.h"
77 #endif
78 #ifdef APDK_LJZJS_MONO
79 #include "ljzjsmono.h"
80 #endif
81 #ifdef APDK_LJZJS_COLOR
82 #include "ljzjscolor.h"
83 #endif
84 #ifdef APDK_LJM1005
85 #include "ljm1005.h"
86 #include "ljp1xxx.h"
87 #endif
88
89 #ifdef APDK_QUICKCONNECT
90 #include "quickconnect.h"
91 #endif
92
93 APDK_BEGIN_NAMESPACE
94
95 #ifdef APDK_PSCRIPT
96 PScriptProxy DeviceRegistry::s_PScriptProxy;
97 #endif
98
99 #ifdef APDK_LJMONO
100 LJMonoProxy DeviceRegistry::s_LJMonoProxy;
101 #endif
102
103 #ifdef APDK_LJCOLOR
104 LJColorProxy DeviceRegistry::s_LJColorProxy;
105 #endif
106
107 #ifdef APDK_LJJETREADY
108 LJJetReadyProxy DeviceRegistry::s_LJJetReadyProxy;
109 #endif
110
111 #ifdef APDK_LJFASTRASTER
112 LJFastRasterProxy DeviceRegistry::s_LJFastRasterProxy;
113 #endif
114
115 #ifdef APDK_LJZJS_MONO
116 LJZjsMonoProxy DeviceRegistry::s_LJZjsMonoProxy;
117 #endif
118
119 #ifdef APDK_LJZJS_COLOR
120 LJZjsColorProxy DeviceRegistry::s_LJZjsColorProxy;
121 #endif
122
123 #ifdef APDK_LJM1005
124 LJM1005Proxy DeviceRegistry::s_LJM1005Proxy;
125 LJP1XXXProxy DeviceRegistry::s_LJP1XXXProxy;
126 #endif
127
128 #if defined(APDK_PSP100) && defined (APDK_DJ9xxVIP)
129 PSP100Proxy DeviceRegistry::s_PSP100Proxy;
130 PSP470Proxy DeviceRegistry::s_PSP470Proxy;
131 #endif
132
133 #if defined(APDK_DJGENERICVIP) && defined (APDK_DJ9xxVIP)
134 DJGenericVIPProxy DeviceRegistry::s_DJGenericVIPProxy;
135 DJ55xxProxy DeviceRegistry::s_DJ55xxProxy;
136 #endif
137
138 #ifdef APDK_DJ9xx
139 DJ9xxProxy DeviceRegistry::s_DJ9xxProxy;
140 #endif
141
142 #ifdef APDK_DJ9xxVIP
143 DJ9xxVIPProxy DeviceRegistry::s_DJ9xxVIPProxy;
144 OJProKx50Proxy DeviceRegistry::s_OJProKx50Proxy;
145 #endif
146
147 #if defined(APDK_DJ8xx)|| defined(APDK_DJ9xx)
148 DJ8xxProxy DeviceRegistry::s_DJ8xxProxy;
149 #endif
150
151 #if defined(APDK_DJ8xx)|| defined(APDK_DJ9xx)
152 #ifdef APDK_DJ8x5
153 DJ8x5Proxy DeviceRegistry::s_DJ8x5Proxy;
154 #endif
155 #endif
156
157 #if defined(APDK_DJ890)
158 DJ890Proxy DeviceRegistry::s_DJ890Proxy;
159 #endif
160
161 #if defined(APDK_DJ850)
162 DJ850Proxy DeviceRegistry::s_DJ850Proxy;
163 #endif
164
165 #ifdef APDK_DJ6xxPhoto
166 DJ6xxPhotoProxy DeviceRegistry::s_DJ6xxPhotoProxy;
167 #endif
168
169 #ifdef APDK_DJ6xx
170 DJ660Proxy DeviceRegistry::s_DJ660Proxy;
171 #endif
172
173 #ifdef APDK_DJ630
174 DJ630Proxy DeviceRegistry::s_DJ630Proxy;
175 #endif
176
177 #ifdef APDK_DJ600
178 DJ600Proxy DeviceRegistry::s_DJ600Proxy;
179 #endif
180
181 #ifdef APDK_DJ540
182 DJ540Proxy DeviceRegistry::s_DJ540Proxy;
183 #endif
184
185 #ifdef APDK_DJ400
186 DJ400Proxy DeviceRegistry::s_DJ400Proxy;
187 #endif
188
189 #ifdef APDK_DJ350
190 DJ350Proxy DeviceRegistry::s_DJ350Proxy;
191 #endif
192
193 #if defined(APDK_DJ3600) && defined (APDK_DJ3320)
194 DJ3600Proxy DeviceRegistry::s_DJ3600Proxy;
195 DJ4100Proxy DeviceRegistry::s_DJ4100Proxy;
196 DJD2600Proxy DeviceRegistry::s_DJD2600Proxy;
197 #endif
198
199 #if defined (APDK_DJ3320)
200 DJ3320Proxy DeviceRegistry::s_DJ3320Proxy;
201 #endif
202
203 #ifdef APDK_APOLLO2560
204 Apollo2560Proxy DeviceRegistry::s_Apollo2560Proxy;
205 #endif
206
207 #ifdef APDK_APOLLO21XX
208 Apollo21xxProxy DeviceRegistry::s_Apollo21xxProxy;
209 #endif
210
211 #ifdef APDK_APOLLO2XXX
212 Apollo2xxxProxy DeviceRegistry::s_Apollo2xxxProxy;
213 #endif
214
215 #ifdef APDK_QUICKCONNECT
216 QuickConnectProxy DeviceRegistry::s_QuickConnectProxy;
217 #endif
218
219 DeviceRegistry::DeviceRegistry()
220     : device(UNSUPPORTED)
221 {
222 }
223
224
225 DeviceRegistry::~DeviceRegistry()
226 {
227     DBG1("deleting DeviceRegistry\n");
228 }
229
230
231 DRIVER_ERROR DeviceRegistry::SelectDevice(const PRINTER_TYPE Model)
232 {
233     if (Model > MAX_PRINTER_TYPE)
234         return UNSUPPORTED_PRINTER;
235     device = Model;
236
237         return NO_ERROR;
238 }
239
240
241 DRIVER_ERROR DeviceRegistry::SelectDevice(char* model, int *pVIPVersion, char* pens, SystemServices* pSS)
242 // used by PrintContext constructor
243 // based on this 'model' string, we will search for the enum'd value
244 // and set this enum'd value in 'device'
245 {
246
247 #if defined(DEBUG) && (DBG_MASK & DBG_LVL1)
248     printf("DR::SelectDevice: model= '%s'\n",model);
249     printf("DR::SelectDevice: VIPver= %d\n",*pVIPVersion);
250     printf("DR::SelectDevice: pens= '%s'\n",pens);
251 #endif
252
253         int j = 0;
254     char pen1 = '\0';   // black/color(for CCM)/photo(for 690) pen
255     char pen2 = '\0';   // color/non-existent(for CCM) pen
256
257     BOOL match=FALSE;
258
259     DRIVER_ERROR err = NO_ERROR;
260
261     FAMILY_HANDLE familyHandle = pPFI->FindDevIdMatch(model);
262     if (familyHandle != NULL)
263     {
264                 device = pPFI->GetFamilyType(familyHandle);
265                 match = TRUE;
266         }
267
268     if (!match) // see if printer supports VIP, if so set compatible device
269     {
270         if (*pVIPVersion == 1)
271         {
272             match = TRUE;
273             device = eDJ9xxVIP;
274         }
275         else if (*pVIPVersion > 1)
276         {
277             match = TRUE;
278             device = eDJGenericVIP; // eDJ9xxVIP;
279         }
280     }
281
282 /*
283  *  See if this is a sleek (LIDIL) printer, or PostScript printer
284  */
285
286     if (!match)
287     {
288         BYTE DevIDBuffer[DevIDBuffSize];
289
290         err = pSS->GetDeviceID(DevIDBuffer, DevIDBuffSize, FALSE);
291         ERRCHECK;   // should be either NO_ERROR or BAD_DEVICE_ID
292
293                 char    *cmdStr = (char *) strstr ((const char *) DevIDBuffer+2, "CMD:");
294         char    *cmdStrEnd;
295         if ((strstr((const char *) DevIDBuffer+2,"CMD:LDL")))
296         {
297             device = eDJ3320;
298             match = TRUE;
299         }
300         if (!match && cmdStr && (cmdStrEnd = (char *) strstr (cmdStr, ";")))
301         {
302             *cmdStrEnd = '\0';
303             if (strstr (cmdStr, "LDL"))
304             {
305                 match = TRUE;
306                 device = eDJ4100;
307             }
308             *cmdStrEnd = ';';
309         }
310                 if (!match && !cmdStr)
311                 {
312                         cmdStr = (char *) strstr ((const char *) DevIDBuffer+2, "COMMAND SET:");
313                 }
314                 if (!match && cmdStr && (strstr ((const char *) cmdStr+4, "POSTSCRIPT") || 
315                                              strstr ((const char *) cmdStr+4, "PostScript") || 
316                                                      strstr ((const char *) cmdStr+4, "Postscript") || 
317                                                      strstr ((const char *) cmdStr+4, "postscript") ))
318                 {
319                         device = ePScript;
320                         match = TRUE;
321                 }
322     }
323
324     if (!match)
325     {
326     // The devID model string did not have a match for a known printer
327     // and the printer doesn't support VIP so let's look at the pen info for clues
328
329         // if we don't have pen info (VSTATUS) it's presumably
330         //  either sleek, DJ4xx or non-HP
331                 device = UNSUPPORTED;
332         if ( pens[0] != '\0' )
333         {
334             // DJ8xx (and DJ970?) printers return penID $X0$X0
335             //  when powered off
336             if(pens[1] == 'X')
337             {
338                 DBG1("DR:(Unknown Model) Need to do a POWER ON to get penIDs\n");
339
340                 DWORD length=sizeof(DJ895_Power_On);
341                 err = pSS->ToDevice(DJ895_Power_On, &length);
342                 ERRCHECK;
343
344                 err = pSS->FlushIO();
345                 ERRCHECK;
346
347                 // give the printer some time to power up
348                 if (pSS->BusyWait((DWORD)1000) == JOB_CANCELED)
349                 return JOB_CANCELED;
350
351                 // we must re-query the devID
352                 err=GetPrinterModel(model,pVIPVersion,pens,pSS);
353                 ERRCHECK;
354             }
355
356             // Arggghh.  The pen(s) COULD be missing
357             do
358             {
359
360 //                              Is this binary-encoded format?
361
362                                 if (pens[0] != '$')
363                                 {
364                                         break;
365                                 }
366
367                 // get pen1 - penID format is $HB0$FC0
368                 pen1=pens[1];
369
370                 // get pen2 - if it exists
371                 j=2;
372                 BOOL NO_PEN2 = FALSE;
373                 while(pens[j] != '$')   // handles variable length penIDs
374                 {
375                     j++;
376                     if ( pens[j] == '\0' )
377                     // never found a pen2
378                     {
379                         pen2 = '\0';
380                         NO_PEN2 = TRUE;
381                         break;
382                     }
383                 }
384                 if (NO_PEN2 == FALSE)
385                 {
386                     j++;
387                     pen2 = pens[j];
388                 }
389
390                 if(pen1 == 'A' || pen2 == 'A')
391                 {
392                     if(pen1 == 'A')
393                     {
394                         // 2-pen printer with both pens missing
395                         if(pen2 == 'A')
396                             pSS->DisplayPrinterStatus(DISPLAY_NO_PENS);
397
398                         // 1-pen printer with missing pen
399                         else if(pen2 == '\0')
400                             pSS->DisplayPrinterStatus(DISPLAY_NO_PEN_DJ600);
401
402                                                 // may be one-pen DJ8xx derivative
403                                                 else if (pen2 == 'F')
404                                                 {
405                                                         device = eDJ8x5;
406                                                         return NO_ERROR;
407                                                 }
408                         // 2-pen printer with BLACK missing
409                         else pSS->DisplayPrinterStatus(DISPLAY_NO_BLACK_PEN);
410                     }
411                     // 2-pen printer with COLOR missing
412                     else if(pen2 == 'A')
413                                         {
414
415 //                                              possibly DJ8x5 derivative
416
417                                                 if (pen1 == 'H' || pen1 == 'Z' || pen1 == 'L')
418                                                 {
419                                                         device = eDJ8x5;
420                                                         return NO_ERROR;
421                                                 }
422                         pSS->DisplayPrinterStatus(DISPLAY_NO_COLOR_PEN);
423                                         }
424
425                     if (pSS->BusyWait(500) == JOB_CANCELED)
426                         return  JOB_CANCELED;
427
428                     // we must re-query the devID
429                     err=GetPrinterModel(model,pVIPVersion,pens,pSS);
430                     ERRCHECK;
431                 }
432
433             } while(pen1 == 'A' || pen2 == 'A');
434
435             // now that we have pens to look at, let's do the logic
436             //  to instantiate the 'best-fit' driver
437
438             if (pen1 == 'H' || pen1 == 'Z' || pen1 == 'L') // (BLACK)
439             {
440                 // check for a 850/855/870
441                 if (pen2 == 'M')
442                                         device = eDJ850;
443                 else if (strncmp (model,"DESKJET 890",11) == 0)
444                     device=eDJ890; // 890 has same pens as DJ895!
445                 else if (pen2 == 'N')   // (COLOR)
446                                         device = eDJ9xx;
447                 // It must be a DJ8xx derivative or will hopefully at
448                 // least recognize a DJ8xx print mode
449                 else
450                                         device = eDJ8xx;
451             }
452             else if(pen1 == 'C') // (BLACK)
453             {
454                 // check for 1-pen printer
455                 if (pen2 == '\0') device = eDJ600;
456                 // must be a 2-pen 6xx-derivative
457                 else device = eDJ6xx;
458             }
459             else if(pen1 == 'M') // Multi-dye load
460             {
461                 // must be a 690-derivative
462                 device = eDJ6xxPhoto;
463             }
464
465             // check for 540-style pens?
466             //  D = color, E = black
467
468 //            else device=UNSUPPORTED;
469         }
470     }
471
472
473     // Early DJ8xx printer do not yet have full bi-di so check
474     // the model to avoid a communication problem.
475     if ( ( (strncmp(model,"DESKJET 81",10) == 0)
476         || (strncmp(model,"DESKJET 83",10) == 0)
477         || (strncmp(model,"DESKJET 88",10) == 0)
478         || (strncmp(model,"DESKJET 895",11) == 0)
479          )
480         && (pSS->IOMode.bUSB)
481        )
482     {
483         DBG1("This printer has limited USB status\n");
484         pSS->IOMode.bStatus = FALSE;
485         pSS->IOMode.bDevID = FALSE;
486     }
487
488     if ( ( (strncmp(model,"DESKJET 63",10) == 0)
489         || (strncmp(model,"DESKJET 64",10) == 0)
490          )
491         && (pSS->IOMode.bUSB)
492        )
493     {
494         DBG1("This printer has limited USB status, but we did get DeviceIDString\n");
495         pSS->IOMode.bStatus = FALSE;
496     }
497
498     if (device == UNSUPPORTED) return UNSUPPORTED_PRINTER;
499     else return NO_ERROR;
500 } //SelectDevice
501
502 DRIVER_ERROR DeviceRegistry::SelectDevice(const char* sDevID, SystemServices* pSS)
503 {
504     char strModel[DevIDBuffSize]; // to contain the MODEL (MDL) from the DevID
505     char strPens[64];   // to contain the VSTATUS penID from the DevID
506     int  VIPVersion;    // VIP version from the DevID
507
508         DRIVER_ERROR err = ParseDevIDString(sDevID, strModel, &VIPVersion, strPens);
509         if (err != NO_ERROR)
510         {
511                 return UNSUPPORTED_PRINTER;
512         }
513
514         return SelectDevice(strModel, &VIPVersion, strPens, pSS);
515 }
516
517 DRIVER_ERROR DeviceRegistry::InstantiatePrinter(Printer*& p, SystemServices* pSS)
518 // Instantiate a printer object and return a pointer p based on the previously
519 // set 'device' variable
520 {
521     //ASSERT(p == NULL);  // if it's not then we're going to loose memory
522
523     FAMILY_HANDLE familyHandle = pPFI->FindDevIdMatch(ModelName[device]);
524     if (familyHandle == NULL)
525     {
526         ASSERT(familyHandle);
527         DBG1("DR::InstantiatePrinter - no family match\n");
528         return UNSUPPORTED_PRINTER;
529     }
530     p = pPFI->CreatePrinter(pSS, familyHandle);
531     NEWCHECK(p);
532     return p->constructor_error;
533 } //InstantiatePrinter
534
535
536
537 DRIVER_ERROR DeviceRegistry::GetPrinterModel(char* strModel, int *pVIPVersion, char* strPens, SystemServices* pSS)
538 {
539     DRIVER_ERROR err;
540     BYTE DevIDBuffer[DevIDBuffSize];
541
542     err = pSS->GetDeviceID(DevIDBuffer, DevIDBuffSize, TRUE);
543     ERRCHECK;   // should be either NO_ERROR or BAD_DEVICE_ID
544
545     return ParseDevIDString((const char*)DevIDBuffer, strModel, pVIPVersion, strPens);
546
547 } //GetPrinterModel
548
549 #define HEXTOINT(x, p) if (x >= '0' && x <= '9')      *p |= x - '0'; \
550                        else if (x >= 'A' && x <= 'F') *p |= 0xA + x - 'A'; \
551                        else if (x >= 'a' && x <= 'f') *p |= 0xA + x - 'a'
552
553
554 //ParseDevIDString
555 //! Parse a device id string
556 /*!
557 Enter a full description of the method here. This will be the API doc.
558
559 ******************************************************************************/
560 DRIVER_ERROR DeviceRegistry::ParseDevIDString(const char* sDevID, char* strModel, int *pVIPVersion, char* strPens)
561 {
562     int i;  // simple counter
563     char* pStr = NULL;  // string pointer used in parsing DevID
564
565     // get the model name
566     // - note: I'm setting pStr to the return of strstr
567     //   so I need to increment past my search string
568     if ( (pStr = (char *)strstr(sDevID+2,"MODEL:")) )
569         pStr+=6;
570     else
571         if ( (pStr=(char *)strstr(sDevID+2,"MDL:")) )
572             pStr+=4;
573         else return BAD_DEVICE_ID;
574
575     // my own version of strtok to pull out the model string here
576     i = 0;
577     while ( (pStr[i] != ';') && (pStr[i] != '\0') && (i < DevIDBuffSize))
578         {
579         strModel[i] = pStr[i];
580                 i++;
581         }
582     strModel[i] = '\0';
583
584
585     // see if this printer support VIP or not
586     if ( (pStr=(char *)strstr(sDevID+2,";S:00")) )   // binary encoded device ID status (version 0)
587     {
588         pStr += 15;     // get to the VIP support field (version of 0 == doesn't support VIP)
589         if ((*pStr >= '0') && (*pStr <= '9'))
590         {
591             *pVIPVersion = *pStr - '0';
592         }
593         else if ((*pStr >= 'A') && (*pStr <= 'F'))
594         {
595             *pVIPVersion = 10 + (*pStr - 'A');
596         }
597         else
598         {
599             *pVIPVersion = 0;
600         }
601     }
602
603 /*
604  *  DevID string has changed starting with Jupiter.
605  *  Following ";S:", two nibbles for Version Number
606  *  12 nibbles for Status Information, the last nibble
607  *  is reserved for future use, the second from last
608  *  indicates whether the printer has VIP support.
609  *
610  *  Actually, four nibbles were added.
611  *  The first fourteen nibbles contain feature state info.
612  *  So, starting with version 02 of device id, following ":S:" there are
613  *   2 nibbles for version number
614  *  14 nibbles for feature state  (the 15th nibble from ';' is the vip flag
615  *   2 nibbles for printer status
616  *
617  *  Crystal added 4 more nibbles to the option field.
618  */
619
620     else if ((pStr = (char *)strstr (sDevID+2, ";S:")))
621     {
622         *pVIPVersion = 0;
623         HEXTOINT (*(pStr+3), pVIPVersion);
624         *pVIPVersion = *pVIPVersion << 4;
625         HEXTOINT (*(pStr+4), pVIPVersion);
626         if (*(pStr + 15) == '1')
627         {
628             (*pVIPVersion)++;
629         }
630         else
631         {
632             *pVIPVersion = 0;
633         }
634     }
635
636     else
637     {
638         *pVIPVersion = 0;
639     }
640
641     // now get the pen info
642     if( (pStr=(char *)strstr(sDevID+2,"VSTATUS:")) )
643     {
644         pStr+=8;
645         i=0;
646         while ( (pStr[i] != ',') && (pStr[i] != ';') && (pStr[i] != '\0') )
647                 {
648             strPens[i] = pStr[i];
649                         i++;
650                 }
651         strPens[i] = '\0';
652     }
653     else if ( (pStr = (char *)strstr(sDevID + 2, ";S:00")) ||   // binary encoded device ID status (version 0)
654              (pStr = (char *)strstr (sDevID + 2, ";S:")))  // Jupiter and later style
655     {
656
657         int     iVersion = 0;
658         HEXTOINT (*(pStr+3), &iVersion);
659         iVersion = iVersion << 4;
660         HEXTOINT (*(pStr+4), &iVersion);
661         if (iVersion <= 2)
662         {
663             pStr += 19;     // get to the number of pens field
664         }
665         else if (iVersion < 4)
666         {
667             pStr += 21;
668         }
669         else
670         {
671             pStr += 25;
672         }
673
674         // each supported pen has a block of 8 bytes of info so copy the number of pens byte
675         // plus 8 bytes for each supported ped
676         if ((*pStr >= '0') && (*pStr <= '9'))
677         {
678             i = 1 + ((*pStr-'0')*8);
679         }
680         else if ((*pStr >= 'A') && (*pStr <= 'F'))
681         {
682             i = 1 + ((10 + (*pStr-'A')) * 8);
683         }
684         else
685         {   // bogus number of pens field
686             i = 1;
687         }
688         memcpy(strPens, pStr, i);
689         strPens[i] = '\0';
690     }
691     else   // no VSTATUS for 400 and sleek printers
692         strPens[0] = '\0';
693
694     return NO_ERROR;
695 } //ParseDevIDString
696
697 APDK_END_NAMESPACE
698