Revert manifest to default one
[external/cups.git] / backend / ieee1284.c
1 /*
2  * "$Id: ieee1284.c 9828 2011-06-14 06:38:24Z mike $"
3  *
4  *   IEEE-1284 support functions for CUPS.
5  *
6  *   Copyright 2007-2011 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   "LICENSE" which should have been included with this file.  If this
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  *   This file is subject to the Apple OS-Developed Software exception.
16  *
17  * Contents:
18  *
19  *   backendGetDeviceID()  - Get the IEEE-1284 device ID string and
20  *                           corresponding URI.
21  *   backendGetMakeModel() - Get the make and model string from the device ID.
22  */
23
24 /*
25  * Include necessary headers.
26  */
27
28 #include "backend-private.h"
29 #include <cups/cups-private.h>
30
31
32 /*
33  * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and
34  *                          corresponding URI.
35  */
36
37 int                                     /* O - 0 on success, -1 on failure */
38 backendGetDeviceID(
39     int        fd,                      /* I - File descriptor */
40     char       *device_id,              /* O - 1284 device ID */
41     int        device_id_size,          /* I - Size of buffer */
42     char       *make_model,             /* O - Make/model */
43     int        make_model_size,         /* I - Size of buffer */
44     const char *scheme,                 /* I - URI scheme */
45     char       *uri,                    /* O - Device URI */
46     int        uri_size)                /* I - Size of buffer */
47 {
48 #ifdef __APPLE__ /* This function is a no-op */
49   (void)fd;
50   (void)device_id;
51   (void)device_id_size;
52   (void)make_model;
53   (void)make_model_size;
54   (void)scheme;
55   (void)uri;
56   (void)uri_size;
57
58   return (-1);
59
60 #else /* Get the device ID from the specified file descriptor... */
61 #  ifdef __linux
62   int   length;                         /* Length of device ID info */
63   int   got_id = 0;
64 #  endif /* __linux */
65 #  if defined(__sun) && defined(ECPPIOC_GETDEVID)
66   struct ecpp_device_id did;            /* Device ID buffer */
67 #  endif /* __sun && ECPPIOC_GETDEVID */
68   char  *ptr;                           /* Pointer into device ID */
69
70
71   DEBUG_printf(("backendGetDeviceID(fd=%d, device_id=%p, device_id_size=%d, "
72                 "make_model=%p, make_model_size=%d, scheme=\"%s\", "
73                 "uri=%p, uri_size=%d)\n", fd, device_id, device_id_size,
74                 make_model, make_model_size, scheme ? scheme : "(null)",
75                 uri, uri_size));
76
77  /*
78   * Range check input...
79   */
80
81   if (!device_id || device_id_size < 32)
82   {
83     DEBUG_puts("backendGetDeviceID: Bad args!");
84     return (-1);
85   }
86
87   if (make_model)
88     *make_model = '\0';
89
90   if (fd >= 0)
91   {
92    /*
93     * Get the device ID string...
94     */
95
96     *device_id = '\0';
97
98 #  ifdef __linux
99     if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id))
100     {
101      /*
102       * Linux has to implement things differently for every device it seems.
103       * Since the standard parallel port driver does not provide a simple
104       * ioctl() to get the 1284 device ID, we have to open the "raw" parallel
105       * device corresponding to this port and do some negotiation trickery
106       * to get the current device ID.
107       */
108
109       if (uri && !strncmp(uri, "parallel:/dev/", 14))
110       {
111         char    devparport[16];         /* /dev/parportN */
112         int     devparportfd,           /* File descriptor for raw device */
113                   mode;                 /* Port mode */
114
115
116        /*
117         * Since the Linux parallel backend only supports 4 parallel port
118         * devices, just grab the trailing digit and use it to construct a
119         * /dev/parportN filename...
120         */
121
122         snprintf(devparport, sizeof(devparport), "/dev/parport%s",
123                  uri + strlen(uri) - 1);
124
125         if ((devparportfd = open(devparport, O_RDWR | O_NOCTTY)) != -1)
126         {
127          /*
128           * Claim the device...
129           */
130
131           if (!ioctl(devparportfd, PPCLAIM))
132           {
133             fcntl(devparportfd, F_SETFL, fcntl(devparportfd, F_GETFL) | O_NONBLOCK);
134
135             mode = IEEE1284_MODE_COMPAT;
136
137             if (!ioctl(devparportfd, PPNEGOT, &mode))
138             {
139              /*
140               * Put the device into Device ID mode...
141               */
142
143               mode = IEEE1284_MODE_NIBBLE | IEEE1284_DEVICEID;
144
145               if (!ioctl(devparportfd, PPNEGOT, &mode))
146               {
147                /*
148                 * Read the 1284 device ID...
149                 */
150
151                 if ((length = read(devparportfd, device_id,
152                                    device_id_size - 1)) >= 2)
153                 {
154                   device_id[length] = '\0';
155                   got_id = 1;
156                 }
157               }
158             }
159
160            /*
161             * Release the device...
162             */
163
164             ioctl(devparportfd, PPRELEASE);
165           }
166
167           close(devparportfd);
168         }
169       }
170     }
171     else
172       got_id = 1;
173
174     if (got_id)
175     {
176      /*
177       * Extract the length of the device ID string from the first two
178       * bytes.  The 1284 spec says the length is stored MSB first...
179       */
180
181       length = (((unsigned)device_id[0] & 255) << 8) +
182                ((unsigned)device_id[1] & 255);
183
184      /*
185       * Check to see if the length is larger than our buffer; first
186       * assume that the vendor incorrectly implemented the 1284 spec,
187       * and then limit the length to the size of our buffer...
188       */
189
190       if (length > device_id_size || length < 14)
191         length = (((unsigned)device_id[1] & 255) << 8) +
192                  ((unsigned)device_id[0] & 255);
193
194       if (length > device_id_size)
195         length = device_id_size;
196
197      /*
198       * The length field counts the number of bytes in the string
199       * including the length field itself (2 bytes).  The minimum
200       * length for a valid/usable device ID is 14 bytes:
201       *
202       *     <LENGTH> MFG: <MFG> ;MDL: <MDL> ;
203       *        2  +   4  +  1  +  5 +  1 +  1
204       */
205
206       if (length < 14)
207       {
208        /*
209         * Can't use this device ID, so don't try to copy it...
210         */
211
212         device_id[0] = '\0';
213         got_id       = 0;
214       }
215       else
216       {
217        /*
218         * Copy the device ID text to the beginning of the buffer and
219         * nul-terminate.
220         */
221
222         length -= 2;
223
224         memmove(device_id, device_id + 2, length);
225         device_id[length] = '\0';
226       }
227     }
228     else
229     {
230       DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n",
231                     strerror(errno)));
232       *device_id = '\0';
233     }
234 #  endif /* __linux */
235
236 #   if defined(__sun) && defined(ECPPIOC_GETDEVID)
237     did.mode = ECPP_CENTRONICS;
238     did.len  = device_id_size - 1;
239     did.rlen = 0;
240     did.addr = device_id;
241
242     if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
243     {
244      /*
245       * Nul-terminate the device ID text.
246       */
247
248       if (did.rlen < (device_id_size - 1))
249         device_id[did.rlen] = '\0';
250       else
251         device_id[device_id_size - 1] = '\0';
252     }
253 #    ifdef DEBUG
254     else
255       DEBUG_printf(("backendGetDeviceID: ioctl failed - %s\n",
256                     strerror(errno)));
257 #    endif /* DEBUG */
258 #  endif /* __sun && ECPPIOC_GETDEVID */
259   }
260
261  /*
262   * Check whether device ID is valid. Turn line breaks and tabs to spaces and
263   * reject device IDs with non-printable characters.
264   */
265
266   for (ptr = device_id; *ptr; ptr ++)
267     if (_cups_isspace(*ptr))
268       *ptr = ' ';
269     else if ((*ptr & 255) < ' ' || *ptr == 127)
270     {
271       DEBUG_printf(("backendGetDeviceID: Bad device_id character %d.",
272                     *ptr & 255));
273       *device_id = '\0';
274       break;
275     }
276
277   DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id));
278
279   if (scheme && uri)
280     *uri = '\0';
281
282   if (!*device_id)
283     return (-1);
284
285  /*
286   * Get the make and model...
287   */
288
289   if (make_model)
290     backendGetMakeModel(device_id, make_model, make_model_size);
291
292  /*
293   * Then generate a device URI...
294   */
295
296   if (scheme && uri && uri_size > 32)
297   {
298     int                 num_values;     /* Number of keys and values */
299     cups_option_t       *values;        /* Keys and values in device ID */
300     const char          *mfg,           /* Manufacturer */
301                         *mdl,           /* Model */
302                         *sern;          /* Serial number */
303     char                temp[256],      /* Temporary manufacturer string */
304                         *tempptr;       /* Pointer into temp string */
305
306
307    /*
308     * Get the make, model, and serial numbers...
309     */
310
311     num_values = _cupsGet1284Values(device_id, &values);
312
313     if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL)
314       if ((sern = cupsGetOption("SERN", num_values, values)) == NULL)
315         sern = cupsGetOption("SN", num_values, values);
316
317     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
318       mfg = cupsGetOption("MFG", num_values, values);
319
320     if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
321       mdl = cupsGetOption("MDL", num_values, values);
322
323     if (mfg)
324     {
325       if (!_cups_strcasecmp(mfg, "Hewlett-Packard"))
326         mfg = "HP";
327       else if (!_cups_strcasecmp(mfg, "Lexmark International"))
328         mfg = "Lexmark";
329     }
330     else
331     {
332       strlcpy(temp, make_model, sizeof(temp));
333
334       if ((tempptr = strchr(temp, ' ')) != NULL)
335         *tempptr = '\0';
336
337       mfg = temp;
338     }
339
340     if (!mdl)
341       mdl = "";
342
343     if (!_cups_strncasecmp(mdl, mfg, strlen(mfg)))
344     {
345       mdl += strlen(mfg);
346
347       while (isspace(*mdl & 255))
348         mdl ++;
349     }
350
351    /*
352     * Generate the device URI from the manufacturer, make_model, and
353     * serial number strings.
354     */
355
356     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, uri_size, scheme, NULL, mfg, 0,
357                      "/%s%s%s", mdl, sern ? "?serial=" : "", sern ? sern : "");
358
359     cupsFreeOptions(num_values, values);
360   }
361
362   return (0);
363 #endif /* __APPLE__ */
364 }
365
366
367 /*
368  * 'backendGetMakeModel()' - Get the make and model string from the device ID.
369  */
370
371 int                                     /* O - 0 on success, -1 on failure */
372 backendGetMakeModel(
373     const char *device_id,              /* O - 1284 device ID */
374     char       *make_model,             /* O - Make/model */
375     int        make_model_size)         /* I - Size of buffer */
376 {
377   int           num_values;             /* Number of keys and values */
378   cups_option_t *values;                /* Keys and values */
379   const char    *mfg,                   /* Manufacturer string */
380                 *mdl,                   /* Model string */
381                 *des;                   /* Description string */
382
383
384   DEBUG_printf(("backendGetMakeModel(device_id=\"%s\", "
385                 "make_model=%p, make_model_size=%d)\n", device_id,
386                 make_model, make_model_size));
387
388  /*
389   * Range check input...
390   */
391
392   if (!device_id || !*device_id || !make_model || make_model_size < 32)
393   {
394     DEBUG_puts("backendGetMakeModel: Bad args!");
395     return (-1);
396   }
397
398   *make_model = '\0';
399
400  /*
401   * Look for the description field...
402   */
403
404   num_values = _cupsGet1284Values(device_id, &values);
405
406   if ((mdl = cupsGetOption("MODEL", num_values, values)) == NULL)
407     mdl = cupsGetOption("MDL", num_values, values);
408
409   if (mdl)
410   {
411    /*
412     * Build a make-model string from the manufacturer and model attributes...
413     */
414
415     if ((mfg = cupsGetOption("MANUFACTURER", num_values, values)) == NULL)
416       mfg = cupsGetOption("MFG", num_values, values);
417
418     if (!mfg || !_cups_strncasecmp(mdl, mfg, strlen(mfg)))
419     {
420      /*
421       * Just copy the model string, since it has the manufacturer...
422       */
423
424       _ppdNormalizeMakeAndModel(mdl, make_model, make_model_size);
425     }
426     else
427     {
428      /*
429       * Concatenate the make and model...
430       */
431
432       char      temp[1024];             /* Temporary make and model */
433
434       snprintf(temp, sizeof(temp), "%s %s", mfg, mdl);
435
436       _ppdNormalizeMakeAndModel(temp, make_model, make_model_size);
437     }
438   }
439   else if ((des = cupsGetOption("DESCRIPTION", num_values, values)) != NULL ||
440            (des = cupsGetOption("DES", num_values, values)) != NULL)
441   {
442    /*
443     * Make sure the description contains something useful, since some
444     * printer manufacturers (HP) apparently don't follow the standards
445     * they helped to define...
446     *
447     * Here we require the description to be 8 or more characters in length,
448     * containing at least one space and one letter.
449     */
450
451     if (strlen(des) >= 8)
452     {
453       const char        *ptr;           /* Pointer into description */
454       int               letters,        /* Number of letters seen */
455                         spaces;         /* Number of spaces seen */
456
457
458       for (ptr = des, letters = 0, spaces = 0; *ptr; ptr ++)
459       {
460         if (isspace(*ptr & 255))
461           spaces ++;
462         else if (isalpha(*ptr & 255))
463           letters ++;
464
465         if (spaces && letters)
466           break;
467       }
468
469       if (spaces && letters)
470         _ppdNormalizeMakeAndModel(des, make_model, make_model_size);
471     }
472   }
473
474   if (!make_model[0])
475   {
476    /*
477     * Use "Unknown" as the printer make and model...
478     */
479
480     strlcpy(make_model, "Unknown", make_model_size);
481   }
482
483   cupsFreeOptions(num_values, values);
484
485   return (0);
486 }
487
488
489 /*
490  * End of "$Id: ieee1284.c 9828 2011-06-14 06:38:24Z mike $".
491  */