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