1 /* $OpenBSD: ulpt.c,v 1.40 2011/09/17 08:36:06 miod Exp $ */ 2 /* $NetBSD: ulpt.c,v 1.57 2003/01/05 10:19:42 scw Exp $ */ 3 /* $FreeBSD: src/sys/dev/usb/ulpt.c,v 1.24 1999/11/17 22:33:44 n_hibma Exp $ */ 4 5 /* 6 * Copyright (c) 1998 The NetBSD Foundation, Inc. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Lennart Augustsson (lennart@augustsson.net) at 11 * Carlstedt Research & Technology. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * Printer Class spec: 37 * http://www.usb.org/developers/devclass_docs/usbprint11.pdf 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/proc.h> 43 #include <sys/kernel.h> 44 #include <sys/device.h> 45 #include <sys/uio.h> 46 #include <sys/conf.h> 47 #include <sys/vnode.h> 48 #include <sys/syslog.h> 49 50 #include <dev/usb/usb.h> 51 #include <dev/usb/usbdi.h> 52 #include <dev/usb/usbdi_util.h> 53 #include <dev/usb/usbdevs.h> 54 #include <dev/usb/usb_quirks.h> 55 56 #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ 57 #define STEP hz/4 58 59 #define LPTPRI (PZERO+8) 60 #define ULPT_BSIZE 16384 61 62 #ifdef ULPT_DEBUG 63 #define DPRINTF(x) do { if (ulptdebug) printf x; } while (0) 64 #define DPRINTFN(n,x) do { if (ulptdebug>(n)) printf x; } while (0) 65 int ulptdebug = 0; 66 #else 67 #define DPRINTF(x) 68 #define DPRINTFN(n,x) 69 #endif 70 71 #define UR_GET_DEVICE_ID 0 72 #define UR_GET_PORT_STATUS 1 73 #define UR_SOFT_RESET 2 74 75 #define LPS_NERR 0x08 /* printer no error */ 76 #define LPS_SELECT 0x10 /* printer selected */ 77 #define LPS_NOPAPER 0x20 /* printer out of paper */ 78 #define LPS_INVERT (LPS_SELECT|LPS_NERR) 79 #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) 80 81 struct ulpt_softc { 82 struct device sc_dev; 83 usbd_device_handle sc_udev; /* device */ 84 usbd_interface_handle sc_iface; /* interface */ 85 int sc_ifaceno; 86 87 int sc_out; 88 usbd_pipe_handle sc_out_pipe; /* bulk out pipe */ 89 90 int sc_in; 91 usbd_pipe_handle sc_in_pipe; /* bulk in pipe */ 92 usbd_xfer_handle sc_in_xfer1; 93 usbd_xfer_handle sc_in_xfer2; 94 u_char sc_junk[64]; /* somewhere to dump input */ 95 96 u_char sc_state; 97 #define ULPT_OPEN 0x01 /* device is open */ 98 #define ULPT_OBUSY 0x02 /* printer is busy doing output */ 99 #define ULPT_INIT 0x04 /* waiting to initialize for open */ 100 u_char sc_flags; 101 #define ULPT_NOPRIME 0x40 /* don't prime on open */ 102 u_char sc_laststatus; 103 104 int sc_refcnt; 105 u_char sc_dying; 106 }; 107 108 void ulpt_disco(void *); 109 110 int ulpt_do_write(struct ulpt_softc *, struct uio *uio, int); 111 int ulpt_status(struct ulpt_softc *); 112 void ulpt_reset(struct ulpt_softc *); 113 int ulpt_statusmsg(u_char, struct ulpt_softc *); 114 115 #if 0 116 void ieee1284_print_id(char *); 117 #endif 118 119 #define ULPTUNIT(s) (minor(s) & 0x1f) 120 #define ULPTFLAGS(s) (minor(s) & 0xe0) 121 122 123 int ulpt_match(struct device *, void *, void *); 124 void ulpt_attach(struct device *, struct device *, void *); 125 int ulpt_detach(struct device *, int); 126 int ulpt_activate(struct device *, int); 127 128 struct cfdriver ulpt_cd = { 129 NULL, "ulpt", DV_DULL 130 }; 131 132 const struct cfattach ulpt_ca = { 133 sizeof(struct ulpt_softc), 134 ulpt_match, 135 ulpt_attach, 136 ulpt_detach, 137 ulpt_activate, 138 }; 139 140 int 141 ulpt_match(struct device *parent, void *match, void *aux) 142 { 143 struct usb_attach_arg *uaa = aux; 144 usb_interface_descriptor_t *id; 145 146 DPRINTFN(10,("ulpt_match\n")); 147 if (uaa->iface == NULL) 148 return (UMATCH_NONE); 149 id = usbd_get_interface_descriptor(uaa->iface); 150 if (id != NULL && 151 id->bInterfaceClass == UICLASS_PRINTER && 152 id->bInterfaceSubClass == UISUBCLASS_PRINTER && 153 ((id->bInterfaceProtocol == UIPROTO_PRINTER_UNI) || 154 (id->bInterfaceProtocol == UIPROTO_PRINTER_BI) || 155 (id->bInterfaceProtocol == UIPROTO_PRINTER_1284))) 156 return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); 157 return (UMATCH_NONE); 158 } 159 160 void 161 ulpt_attach(struct device *parent, struct device *self, void *aux) 162 { 163 struct ulpt_softc *sc = (struct ulpt_softc *)self; 164 struct usb_attach_arg *uaa = aux; 165 usbd_device_handle dev = uaa->device; 166 usbd_interface_handle iface = uaa->iface; 167 usb_interface_descriptor_t *ifcd = usbd_get_interface_descriptor(iface); 168 usb_interface_descriptor_t *id, *iend; 169 usb_config_descriptor_t *cdesc; 170 usbd_status err; 171 usb_endpoint_descriptor_t *ed; 172 u_int8_t epcount; 173 int i, altno; 174 175 DPRINTFN(10,("ulpt_attach: sc=%p\n", sc)); 176 177 //printf("%s: iclass %d/%d\n", sc->sc_dev.dv_xname, 178 // ifcd->bInterfaceClass, ifcd->bInterfaceSubClass); 179 180 /* XXX 181 * Stepping through the alternate settings needs to be abstracted out. 182 */ 183 cdesc = usbd_get_config_descriptor(dev); 184 if (cdesc == NULL) { 185 printf("%s: failed to get configuration descriptor\n", 186 sc->sc_dev.dv_xname); 187 return; 188 } 189 iend = (usb_interface_descriptor_t *) 190 ((char *)cdesc + UGETW(cdesc->wTotalLength)); 191 #ifdef DIAGNOSTIC 192 if (ifcd < (usb_interface_descriptor_t *)cdesc || 193 ifcd >= iend) 194 panic("ulpt: iface desc out of range"); 195 #endif 196 /* Step through all the descriptors looking for bidir mode */ 197 for (id = ifcd, altno = 0; 198 id < iend; 199 id = (void *)((char *)id + id->bLength)) { 200 if (id->bDescriptorType == UDESC_INTERFACE && 201 id->bInterfaceNumber == ifcd->bInterfaceNumber) { 202 if (id->bInterfaceClass == UICLASS_PRINTER && 203 id->bInterfaceSubClass == UISUBCLASS_PRINTER && 204 (id->bInterfaceProtocol == UIPROTO_PRINTER_BI /*|| 205 id->bInterfaceProtocol == UIPROTO_PRINTER_1284*/)) 206 goto found; 207 altno++; 208 } 209 } 210 id = ifcd; /* not found, use original */ 211 found: 212 if (id != ifcd) { 213 /* Found a new bidir setting */ 214 DPRINTF(("ulpt_attach: set altno = %d\n", altno)); 215 err = usbd_set_interface(iface, altno); 216 if (err) { 217 printf("%s: setting alternate interface failed\n", 218 sc->sc_dev.dv_xname); 219 sc->sc_dying = 1; 220 return; 221 } 222 } 223 224 epcount = 0; 225 (void)usbd_endpoint_count(iface, &epcount); 226 227 sc->sc_in = -1; 228 sc->sc_out = -1; 229 for (i = 0; i < epcount; i++) { 230 ed = usbd_interface2endpoint_descriptor(iface, i); 231 if (ed == NULL) { 232 printf("%s: couldn't get ep %d\n", 233 sc->sc_dev.dv_xname, i); 234 return; 235 } 236 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && 237 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 238 sc->sc_in = ed->bEndpointAddress; 239 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && 240 UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { 241 sc->sc_out = ed->bEndpointAddress; 242 } 243 } 244 if (sc->sc_out == -1) { 245 printf("%s: could not find bulk out endpoint\n", 246 sc->sc_dev.dv_xname); 247 sc->sc_dying = 1; 248 return; 249 } 250 251 if (usbd_get_quirks(dev)->uq_flags & UQ_BROKEN_BIDIR) { 252 /* This device doesn't handle reading properly. */ 253 sc->sc_in = -1; 254 } 255 256 printf("%s: using %s-directional mode\n", sc->sc_dev.dv_xname, 257 sc->sc_in >= 0 ? "bi" : "uni"); 258 259 DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_out)); 260 261 sc->sc_iface = iface; 262 sc->sc_ifaceno = id->bInterfaceNumber; 263 sc->sc_udev = dev; 264 265 #if 0 266 /* 267 * This code is disabled because for some mysterious reason it causes 268 * printing not to work. But only sometimes, and mostly with 269 * UHCI and less often with OHCI. *sigh* 270 */ 271 { 272 usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); 273 usb_device_request_t req; 274 int len, alen; 275 276 req.bmRequestType = UT_READ_CLASS_INTERFACE; 277 req.bRequest = UR_GET_DEVICE_ID; 278 USETW(req.wValue, cd->bConfigurationValue); 279 USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); 280 USETW(req.wLength, DEVINFOSIZE - 1); 281 err = usbd_do_request_flags(dev, &req, devinfop, USBD_SHORT_XFER_OK, 282 &alen, USBD_DEFAULT_TIMEOUT); 283 if (err) { 284 printf("%s: cannot get device id\n", sc->sc_dev.dv_xname); 285 } else if (alen <= 2) { 286 printf("%s: empty device id, no printer connected?\n", 287 sc->sc_dev.dv_xname); 288 } else { 289 /* devinfop now contains an IEEE-1284 device ID */ 290 len = ((devinfop[0] & 0xff) << 8) | (devinfop[1] & 0xff); 291 if (len > DEVINFOSIZE - 3) 292 len = DEVINFOSIZE - 3; 293 devinfo[len] = 0; 294 printf("%s: device id <", sc->sc_dev.dv_xname); 295 ieee1284_print_id(devinfop+2); 296 printf(">\n"); 297 } 298 } 299 #endif 300 } 301 302 int 303 ulpt_activate(struct device *self, int act) 304 { 305 struct ulpt_softc *sc = (struct ulpt_softc *)self; 306 307 switch (act) { 308 case DVACT_DEACTIVATE: 309 sc->sc_dying = 1; 310 break; 311 } 312 return (0); 313 } 314 315 int 316 ulpt_detach(struct device *self, int flags) 317 { 318 struct ulpt_softc *sc = (struct ulpt_softc *)self; 319 int s; 320 int maj, mn; 321 322 DPRINTF(("ulpt_detach: sc=%p\n", sc)); 323 324 if (sc->sc_out_pipe != NULL) 325 usbd_abort_pipe(sc->sc_out_pipe); 326 if (sc->sc_in_pipe != NULL) 327 usbd_abort_pipe(sc->sc_in_pipe); 328 329 s = splusb(); 330 if (--sc->sc_refcnt >= 0) { 331 /* There is noone to wake, aborting the pipe is enough */ 332 /* Wait for processes to go away. */ 333 usb_detach_wait(&sc->sc_dev); 334 } 335 splx(s); 336 337 /* locate the major number */ 338 for (maj = 0; maj < nchrdev; maj++) 339 if (cdevsw[maj].d_open == ulptopen) 340 break; 341 342 /* Nuke the vnodes for any open instances (calls close). */ 343 mn = self->dv_unit; 344 vdevgone(maj, mn, mn, VCHR); 345 vdevgone(maj, mn | ULPT_NOPRIME , mn | ULPT_NOPRIME, VCHR); 346 347 return (0); 348 } 349 350 int 351 ulpt_status(struct ulpt_softc *sc) 352 { 353 usb_device_request_t req; 354 usbd_status err; 355 u_char status; 356 357 req.bmRequestType = UT_READ_CLASS_INTERFACE; 358 req.bRequest = UR_GET_PORT_STATUS; 359 USETW(req.wValue, 0); 360 USETW(req.wIndex, sc->sc_ifaceno); 361 USETW(req.wLength, 1); 362 err = usbd_do_request(sc->sc_udev, &req, &status); 363 DPRINTFN(1, ("ulpt_status: status=0x%02x err=%d\n", status, err)); 364 if (!err) 365 return (status); 366 else 367 return (0); 368 } 369 370 void 371 ulpt_reset(struct ulpt_softc *sc) 372 { 373 usb_device_request_t req; 374 375 DPRINTFN(1, ("ulpt_reset\n")); 376 req.bRequest = UR_SOFT_RESET; 377 USETW(req.wValue, 0); 378 USETW(req.wIndex, sc->sc_ifaceno); 379 USETW(req.wLength, 0); 380 381 /* 382 * There was a mistake in the USB printer 1.0 spec that gave the 383 * request type as UT_WRITE_CLASS_OTHER; it should have been 384 * UT_WRITE_CLASS_INTERFACE. Many printers use the old one, 385 * so we try both. 386 */ 387 req.bmRequestType = UT_WRITE_CLASS_OTHER; 388 if (usbd_do_request(sc->sc_udev, &req, 0)) { /* 1.0 */ 389 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 390 (void)usbd_do_request(sc->sc_udev, &req, 0); /* 1.1 */ 391 } 392 } 393 394 static void 395 ulpt_input(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) 396 { 397 struct ulpt_softc *sc = priv; 398 399 DPRINTFN(2,("ulpt_input: got some data\n")); 400 /* Do it again. */ 401 if (xfer == sc->sc_in_xfer1) 402 usbd_transfer(sc->sc_in_xfer2); 403 else 404 usbd_transfer(sc->sc_in_xfer1); 405 } 406 407 int ulptusein = 1; 408 409 /* 410 * Reset the printer, then wait until it's selected and not busy. 411 */ 412 int 413 ulptopen(dev_t dev, int flag, int mode, struct proc *p) 414 { 415 u_char flags = ULPTFLAGS(dev); 416 struct ulpt_softc *sc; 417 usbd_status err; 418 int error; 419 420 if (ULPTUNIT(dev) >= ulpt_cd.cd_ndevs) 421 return (ENXIO); 422 sc = ulpt_cd.cd_devs[ULPTUNIT(dev)]; 423 if (sc == NULL) 424 return (ENXIO); 425 426 if (sc == NULL || sc->sc_iface == NULL || sc->sc_dying) 427 return (ENXIO); 428 429 if (sc->sc_state) 430 return (EBUSY); 431 432 sc->sc_state = ULPT_INIT; 433 sc->sc_flags = flags; 434 DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags)); 435 436 error = 0; 437 sc->sc_refcnt++; 438 439 if ((flags & ULPT_NOPRIME) == 0) { 440 ulpt_reset(sc); 441 if (sc->sc_dying) { 442 error = ENXIO; 443 sc->sc_state = 0; 444 goto done; 445 } 446 } 447 448 err = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe); 449 if (err) { 450 sc->sc_state = 0; 451 error = EIO; 452 goto done; 453 } 454 if (ulptusein && sc->sc_in != -1) { 455 DPRINTF(("ulpt_open: open input pipe\n")); 456 err = usbd_open_pipe(sc->sc_iface, sc->sc_in,0,&sc->sc_in_pipe); 457 if (err) { 458 error = EIO; 459 usbd_close_pipe(sc->sc_out_pipe); 460 sc->sc_out_pipe = NULL; 461 sc->sc_state = 0; 462 goto done; 463 } 464 sc->sc_in_xfer1 = usbd_alloc_xfer(sc->sc_udev); 465 sc->sc_in_xfer2 = usbd_alloc_xfer(sc->sc_udev); 466 if (sc->sc_in_xfer1 == NULL || sc->sc_in_xfer2 == NULL) { 467 error = ENOMEM; 468 if (sc->sc_in_xfer1 != NULL) { 469 usbd_free_xfer(sc->sc_in_xfer1); 470 sc->sc_in_xfer1 = NULL; 471 } 472 if (sc->sc_in_xfer2 != NULL) { 473 usbd_free_xfer(sc->sc_in_xfer2); 474 sc->sc_in_xfer2 = NULL; 475 } 476 usbd_close_pipe(sc->sc_out_pipe); 477 sc->sc_out_pipe = NULL; 478 usbd_close_pipe(sc->sc_in_pipe); 479 sc->sc_in_pipe = NULL; 480 sc->sc_state = 0; 481 goto done; 482 } 483 usbd_setup_xfer(sc->sc_in_xfer1, sc->sc_in_pipe, sc, 484 sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK, 485 USBD_NO_TIMEOUT, ulpt_input); 486 usbd_setup_xfer(sc->sc_in_xfer2, sc->sc_in_pipe, sc, 487 sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK, 488 USBD_NO_TIMEOUT, ulpt_input); 489 usbd_transfer(sc->sc_in_xfer1); /* ignore failed start */ 490 } 491 492 sc->sc_state = ULPT_OPEN; 493 494 done: 495 if (--sc->sc_refcnt < 0) 496 usb_detach_wakeup(&sc->sc_dev); 497 498 DPRINTF(("ulptopen: done, error=%d\n", error)); 499 return (error); 500 } 501 502 int 503 ulpt_statusmsg(u_char status, struct ulpt_softc *sc) 504 { 505 u_char new; 506 507 status = (status ^ LPS_INVERT) & LPS_MASK; 508 new = status & ~sc->sc_laststatus; 509 sc->sc_laststatus = status; 510 511 if (new & LPS_SELECT) 512 log(LOG_NOTICE, "%s: offline\n", sc->sc_dev.dv_xname); 513 else if (new & LPS_NOPAPER) 514 log(LOG_NOTICE, "%s: out of paper\n", sc->sc_dev.dv_xname); 515 else if (new & LPS_NERR) 516 log(LOG_NOTICE, "%s: output error\n", sc->sc_dev.dv_xname); 517 518 return (status); 519 } 520 521 int 522 ulptclose(dev_t dev, int flag, int mode, struct proc *p) 523 { 524 struct ulpt_softc *sc; 525 526 sc = ulpt_cd.cd_devs[ULPTUNIT(dev)]; 527 528 if (sc->sc_state != ULPT_OPEN) 529 /* We are being forced to close before the open completed. */ 530 return (0); 531 532 if (sc->sc_out_pipe != NULL) { 533 usbd_close_pipe(sc->sc_out_pipe); 534 sc->sc_out_pipe = NULL; 535 } 536 if (sc->sc_in_pipe != NULL) { 537 usbd_abort_pipe(sc->sc_in_pipe); 538 usbd_close_pipe(sc->sc_in_pipe); 539 sc->sc_in_pipe = NULL; 540 if (sc->sc_in_xfer1 != NULL) { 541 usbd_free_xfer(sc->sc_in_xfer1); 542 sc->sc_in_xfer1 = NULL; 543 } 544 if (sc->sc_in_xfer2 != NULL) { 545 usbd_free_xfer(sc->sc_in_xfer2); 546 sc->sc_in_xfer2 = NULL; 547 } 548 } 549 550 sc->sc_state = 0; 551 552 DPRINTF(("ulptclose: closed\n")); 553 return (0); 554 } 555 556 int 557 ulpt_do_write(struct ulpt_softc *sc, struct uio *uio, int flags) 558 { 559 u_int32_t n; 560 int error = 0; 561 void *bufp; 562 usbd_xfer_handle xfer; 563 usbd_status err; 564 565 DPRINTF(("ulptwrite\n")); 566 xfer = usbd_alloc_xfer(sc->sc_udev); 567 if (xfer == NULL) 568 return (ENOMEM); 569 bufp = usbd_alloc_buffer(xfer, ULPT_BSIZE); 570 if (bufp == NULL) { 571 usbd_free_xfer(xfer); 572 return (ENOMEM); 573 } 574 while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) { 575 ulpt_statusmsg(ulpt_status(sc), sc); 576 error = uiomove(bufp, n, uio); 577 if (error) 578 break; 579 DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n)); 580 err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, USBD_NO_COPY, 581 USBD_NO_TIMEOUT, bufp, &n, "ulptwr"); 582 if (err) { 583 DPRINTF(("ulptwrite: error=%d\n", err)); 584 error = EIO; 585 break; 586 } 587 } 588 usbd_free_xfer(xfer); 589 590 return (error); 591 } 592 593 int 594 ulptwrite(dev_t dev, struct uio *uio, int flags) 595 { 596 struct ulpt_softc *sc; 597 int error; 598 599 sc = ulpt_cd.cd_devs[ULPTUNIT(dev)]; 600 601 if (sc->sc_dying) 602 return (EIO); 603 604 sc->sc_refcnt++; 605 error = ulpt_do_write(sc, uio, flags); 606 if (--sc->sc_refcnt < 0) 607 usb_detach_wakeup(&sc->sc_dev); 608 return (error); 609 } 610 611 #if 0 612 /* XXX This does not belong here. */ 613 /* 614 * Print select parts of a IEEE 1284 device ID. 615 */ 616 void 617 ieee1284_print_id(char *str) 618 { 619 char *p, *q; 620 621 for (p = str-1; p; p = strchr(p, ';')) { 622 p++; /* skip ';' */ 623 if (strncmp(p, "MFG:", 4) == 0 || 624 strncmp(p, "MANUFACTURER:", 14) == 0 || 625 strncmp(p, "MDL:", 4) == 0 || 626 strncmp(p, "MODEL:", 6) == 0) { 627 q = strchr(p, ';'); 628 if (q) 629 printf("%.*s", (int)(q - p + 1), p); 630 } 631 } 632 } 633 #endif 634