162642Sn_hibma/* $NetBSD: usbhid.c,v 1.14 2000/07/03 02:51:37 matt Exp $ */ 262642Sn_hibma/* $FreeBSD$ */ 362642Sn_hibma 462642Sn_hibma/* 562642Sn_hibma * Copyright (c) 1998 The NetBSD Foundation, Inc. 662642Sn_hibma * All rights reserved. 762642Sn_hibma * 862642Sn_hibma * This code is derived from software contributed to The NetBSD Foundation 962642Sn_hibma * by Lennart Augustsson (augustss@netbsd.org). 1062642Sn_hibma * 1162642Sn_hibma * Redistribution and use in source and binary forms, with or without 1262642Sn_hibma * modification, are permitted provided that the following conditions 1362642Sn_hibma * are met: 1462642Sn_hibma * 1. Redistributions of source code must retain the above copyright 1562642Sn_hibma * notice, this list of conditions and the following disclaimer. 1662642Sn_hibma * 2. Redistributions in binary form must reproduce the above copyright 1762642Sn_hibma * notice, this list of conditions and the following disclaimer in the 1862642Sn_hibma * documentation and/or other materials provided with the distribution. 1962642Sn_hibma * 2062642Sn_hibma * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2162642Sn_hibma * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2262642Sn_hibma * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2362642Sn_hibma * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2462642Sn_hibma * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2562642Sn_hibma * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2662642Sn_hibma * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2762642Sn_hibma * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2862642Sn_hibma * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2962642Sn_hibma * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3062642Sn_hibma * POSSIBILITY OF SUCH DAMAGE. 3162642Sn_hibma */ 3262642Sn_hibma 3362642Sn_hibma#include <stdio.h> 3462642Sn_hibma#include <stdlib.h> 3562642Sn_hibma#include <string.h> 3662642Sn_hibma#include <sys/types.h> 3762642Sn_hibma#include <fcntl.h> 3862642Sn_hibma#include <unistd.h> 3962642Sn_hibma#include <err.h> 4062642Sn_hibma#include <ctype.h> 4162642Sn_hibma#include <errno.h> 42113273Smdodd#include <usbhid.h> 43188945Sthompsa#include <dev/usb/usbhid.h> 4462642Sn_hibma 45227196Sedstatic struct variable { 46225839Smav char *name; 47225839Smav int instance; 48225839Smav int val; 49225839Smav struct hid_item h; 50225839Smav struct variable *next; 51225839Smav} *vars; 52225839Smav 53227196Sedstatic int verbose = 0; 54227196Sedstatic int noname = 0; 55227196Sedstatic int hexdump = 0; 56227196Sedstatic int wflag = 0; 57227196Sedstatic int zflag = 0; 5862642Sn_hibma 59225839Smavstatic void usage(void); 60225839Smavstatic void dumpitem(const char *label, struct hid_item *h); 61225839Smavstatic void dumpitems(report_desc_t r); 62225839Smavstatic void prdata(u_char *buf, struct hid_item *h); 63225839Smavstatic void dumpdata(int f, report_desc_t r, int loop); 64225839Smavstatic void writedata(int f, report_desc_t r); 6562642Sn_hibma 66225839Smavstatic void 67225839Smavparceargs(report_desc_t r, int all, int nnames, char **names) 6862642Sn_hibma{ 69225839Smav struct hid_data *d; 70225839Smav struct hid_item h; 71225839Smav char colls[1000]; 72225839Smav char hname[1000], *tmp1, *tmp2; 73225839Smav struct variable *var, **pnext; 74225839Smav int i, instance, cp, t; 7562642Sn_hibma 76225839Smav pnext = &vars; 77225839Smav if (all) { 78225839Smav if (wflag) 79225839Smav errx(1, "Must not specify -w to read variables"); 80225839Smav cp = 0; 81225839Smav for (d = hid_start_parse(r, 82225839Smav 1<<hid_input | 1<<hid_output | 1<<hid_feature, -1); 83225839Smav hid_get_item(d, &h); ) { 84225839Smav if (h.kind == hid_collection) { 85225839Smav cp += sprintf(&colls[cp], "%s%s:%s", 86225839Smav cp != 0 ? "." : "", 87225839Smav hid_usage_page(HID_PAGE(h.usage)), 88225839Smav hid_usage_in_page(h.usage)); 89225839Smav } else if (h.kind == hid_endcollection) { 90225839Smav tmp1 = strrchr(colls, '.'); 91225839Smav if (tmp1 != NULL) { 92225839Smav cp -= strlen(tmp1); 93225839Smav tmp1[0] = 0; 94225839Smav } else { 95225839Smav cp = 0; 96225839Smav colls[0] = 0; 97225839Smav } 98225839Smav } 99225839Smav if ((h.kind != hid_input && h.kind != hid_output && 100225839Smav h.kind != hid_feature) || (h.flags & HIO_CONST)) 101225839Smav continue; 102225839Smav var = malloc(sizeof(*var)); 103225839Smav memset(var, 0, sizeof(*var)); 104225839Smav asprintf(&var->name, "%s%s%s:%s", 105225839Smav colls, colls[0] != 0 ? "." : "", 106225839Smav hid_usage_page(HID_PAGE(h.usage)), 107225839Smav hid_usage_in_page(h.usage)); 108225839Smav var->h = h; 109225839Smav *pnext = var; 110225839Smav pnext = &var->next; 111225839Smav } 112225839Smav hid_end_parse(d); 113225839Smav return; 114225839Smav } 115225839Smav for (i = 0; i < nnames; i++) { 116225839Smav var = malloc(sizeof(*var)); 117225839Smav memset(var, 0, sizeof(*var)); 118225839Smav tmp1 = tmp2 = strdup(names[i]); 119225839Smav strsep(&tmp2, "="); 120225839Smav var->name = strsep(&tmp1, "#"); 121225839Smav if (tmp1 != NULL) 122225839Smav var->instance = atoi(tmp1); 123225839Smav if (tmp2 != NULL) { 124225839Smav if (!wflag) 125225839Smav errx(1, "Must specify -w to write variables"); 126225839Smav var->val = atoi(tmp2); 127225839Smav } else 128225839Smav if (wflag) 129225839Smav errx(1, "Must not specify -w to read variables"); 130225839Smav *pnext = var; 131225839Smav pnext = &var->next; 13262642Sn_hibma 133225839Smav instance = 0; 134225839Smav cp = 0; 135225839Smav for (d = hid_start_parse(r, 136225839Smav 1<<hid_input | 1<<hid_output | 1<<hid_feature, -1); 137225839Smav hid_get_item(d, &h); ) { 138225839Smav if (h.kind == hid_collection) { 139225839Smav cp += sprintf(&colls[cp], "%s%s:%s", 140225839Smav cp != 0 ? "." : "", 141225839Smav hid_usage_page(HID_PAGE(h.usage)), 142225839Smav hid_usage_in_page(h.usage)); 143225839Smav } else if (h.kind == hid_endcollection) { 144225839Smav tmp1 = strrchr(colls, '.'); 145225839Smav if (tmp1 != NULL) { 146225839Smav cp -= strlen(tmp1); 147225839Smav tmp1[0] = 0; 148225839Smav } else { 149225839Smav cp = 0; 150225839Smav colls[0] = 0; 151225839Smav } 152225839Smav } 153225839Smav if ((h.kind != hid_input && h.kind != hid_output && 154225839Smav h.kind != hid_feature) || (h.flags & HIO_CONST)) 155225839Smav continue; 156225839Smav snprintf(hname, sizeof(hname), "%s%s%s:%s", 157225839Smav colls, colls[0] != 0 ? "." : "", 158225839Smav hid_usage_page(HID_PAGE(h.usage)), 159225839Smav hid_usage_in_page(h.usage)); 160225839Smav t = strlen(hname) - strlen(var->name); 161225839Smav if (t > 0) { 162225839Smav if (strcmp(hname + t, var->name) != 0) 163225839Smav continue; 164225839Smav if (hname[t - 1] != '.') 165225839Smav continue; 166225839Smav } else if (strcmp(hname, var->name) != 0) 167225839Smav continue; 168225839Smav if (var->instance != instance++) 169225839Smav continue; 170225839Smav var->h = h; 171225839Smav break; 172225839Smav } 173225839Smav hid_end_parse(d); 174225839Smav if (var->h.usage == 0) 175225839Smav errx(1, "Unknown item '%s'", var->name); 176225839Smav } 17762642Sn_hibma} 17862642Sn_hibma 179225839Smavstatic void 18062642Sn_hibmausage(void) 18162642Sn_hibma{ 18262642Sn_hibma 183164531Sgrog fprintf(stderr, 184164531Sgrog "usage: %s -f device " 185235519Smav "[-l] [-n] [-r] [-t tablefile] [-v] [-x] [-z] name ...\n", 186194789Sdelphij getprogname()); 187164531Sgrog fprintf(stderr, 188164531Sgrog " %s -f device " 189235519Smav "[-l] [-n] [-r] [-t tablefile] [-v] [-x] [-z] -a\n", 190194789Sdelphij getprogname()); 191225839Smav fprintf(stderr, 192225839Smav " %s -f device " 193225839Smav "[-t tablefile] [-v] [-z] -w name=value\n", 194225839Smav getprogname()); 19562642Sn_hibma exit(1); 19662642Sn_hibma} 19762642Sn_hibma 198225839Smavstatic void 19987699Smarkmdumpitem(const char *label, struct hid_item *h) 20062642Sn_hibma{ 20162642Sn_hibma if ((h->flags & HIO_CONST) && !verbose) 20262642Sn_hibma return; 203224511Smav printf("%s rid=%d size=%d count=%d page=%s usage=%s%s%s", label, 204224511Smav h->report_ID, h->report_size, h->report_count, 205164531Sgrog hid_usage_page(HID_PAGE(h->usage)), 20662642Sn_hibma hid_usage_in_page(h->usage), 207224511Smav h->flags & HIO_CONST ? " Const" : "", 208224511Smav h->flags & HIO_VARIABLE ? "" : " Array"); 20962642Sn_hibma printf(", logical range %d..%d", 21062642Sn_hibma h->logical_minimum, h->logical_maximum); 21162642Sn_hibma if (h->physical_minimum != h->physical_maximum) 21262642Sn_hibma printf(", physical range %d..%d", 21362642Sn_hibma h->physical_minimum, h->physical_maximum); 21462642Sn_hibma if (h->unit) 21562642Sn_hibma printf(", unit=0x%02x exp=%d", h->unit, h->unit_exponent); 21662642Sn_hibma printf("\n"); 21762642Sn_hibma} 21862642Sn_hibma 219224511Smavstatic const char * 220224511Smavhid_collection_type(int32_t type) 221224511Smav{ 222224511Smav static char num[8]; 223224511Smav 224224511Smav switch (type) { 225224511Smav case 0: return ("Physical"); 226224511Smav case 1: return ("Application"); 227224511Smav case 2: return ("Logical"); 228224511Smav case 3: return ("Report"); 229224511Smav case 4: return ("Named_Array"); 230224511Smav case 5: return ("Usage_Switch"); 231224511Smav case 6: return ("Usage_Modifier"); 232224511Smav } 233224511Smav snprintf(num, sizeof(num), "0x%02x", type); 234224511Smav return (num); 235224511Smav} 236224511Smav 237225839Smavstatic void 23862642Sn_hibmadumpitems(report_desc_t r) 23962642Sn_hibma{ 24062642Sn_hibma struct hid_data *d; 24162642Sn_hibma struct hid_item h; 24267256Sn_hibma int size; 24362642Sn_hibma 244224511Smav for (d = hid_start_parse(r, ~0, -1); hid_get_item(d, &h); ) { 24562642Sn_hibma switch (h.kind) { 24662642Sn_hibma case hid_collection: 247224511Smav printf("Collection type=%s page=%s usage=%s\n", 248224511Smav hid_collection_type(h.collection), 249164531Sgrog hid_usage_page(HID_PAGE(h.usage)), 25062642Sn_hibma hid_usage_in_page(h.usage)); 25162642Sn_hibma break; 25262642Sn_hibma case hid_endcollection: 25362642Sn_hibma printf("End collection\n"); 25462642Sn_hibma break; 25562642Sn_hibma case hid_input: 25662642Sn_hibma dumpitem("Input ", &h); 25762642Sn_hibma break; 25862642Sn_hibma case hid_output: 25962642Sn_hibma dumpitem("Output ", &h); 26062642Sn_hibma break; 26162642Sn_hibma case hid_feature: 26262642Sn_hibma dumpitem("Feature", &h); 26362642Sn_hibma break; 26462642Sn_hibma } 26562642Sn_hibma } 26662642Sn_hibma hid_end_parse(d); 267224511Smav size = hid_report_size(r, hid_input, -1); 26875606Sn_hibma printf("Total input size %d bytes\n", size); 26962642Sn_hibma 270224511Smav size = hid_report_size(r, hid_output, -1); 27167256Sn_hibma printf("Total output size %d bytes\n", size); 27267256Sn_hibma 273224511Smav size = hid_report_size(r, hid_feature, -1); 27467256Sn_hibma printf("Total feature size %d bytes\n", size); 27562642Sn_hibma} 27662642Sn_hibma 277225839Smavstatic void 27862642Sn_hibmaprdata(u_char *buf, struct hid_item *h) 27962642Sn_hibma{ 28062642Sn_hibma u_int data; 28162642Sn_hibma int i, pos; 28262642Sn_hibma 28362642Sn_hibma pos = h->pos; 28462642Sn_hibma for (i = 0; i < h->report_count; i++) { 28562642Sn_hibma data = hid_get_data(buf, h); 286224511Smav if (i > 0) 287224511Smav printf(" "); 28862642Sn_hibma if (h->logical_minimum < 0) 28962642Sn_hibma printf("%d", (int)data); 29062642Sn_hibma else 29162642Sn_hibma printf("%u", data); 292164531Sgrog if (hexdump) 293164531Sgrog printf(" [0x%x]", data); 294224511Smav h->pos += h->report_size; 29562642Sn_hibma } 296224511Smav h->pos = pos; 29762642Sn_hibma} 29862642Sn_hibma 299225839Smavstatic void 30062642Sn_hibmadumpdata(int f, report_desc_t rd, int loop) 30162642Sn_hibma{ 302225839Smav struct variable *var; 303225839Smav int dlen, havedata, i, match, r, rid, use_rid; 30462642Sn_hibma u_char *dbuf; 305225839Smav enum hid_kind kind; 30662642Sn_hibma 307235519Smav kind = zflag ? 3 : 0; 308225839Smav rid = -1; 309225839Smav use_rid = !!hid_get_report_id(f); 310225839Smav do { 311225839Smav if (kind < 3) { 312225839Smav if (++rid >= 256) { 313225839Smav rid = 0; 314225839Smav kind++; 315225839Smav } 316225839Smav if (kind >= 3) 317225839Smav rid = -1; 318225839Smav for (var = vars; var; var = var->next) { 319225839Smav if (rid == var->h.report_ID && 320225839Smav kind == var->h.kind) 321225839Smav break; 322225839Smav } 323225839Smav if (var == NULL) 324225839Smav continue; 325225839Smav } 326225839Smav dlen = hid_report_size(rd, kind < 3 ? kind : hid_input, rid); 327225839Smav if (dlen <= 0) 32862642Sn_hibma continue; 329225839Smav dbuf = malloc(dlen); 330225839Smav memset(dbuf, 0, dlen); 331225839Smav if (kind < 3) { 332225839Smav dbuf[0] = rid; 333225839Smav r = hid_get_report(f, kind, dbuf, dlen); 334225839Smav if (r < 0) 335225839Smav warn("hid_get_report(rid %d)", rid); 336225839Smav havedata = !r && (rid == 0 || dbuf[0] == rid); 337225839Smav if (rid != 0) 338225839Smav dbuf[0] = rid; 339225839Smav } else { 340225839Smav r = read(f, dbuf, dlen); 341225839Smav if (r < 1) 342225839Smav err(1, "read error"); 343225839Smav havedata = 1; 34462642Sn_hibma } 345225839Smav if (verbose) { 346225839Smav printf("Got %s report %d (%d bytes):", 347225839Smav kind == hid_output ? "output" : 348225839Smav kind == hid_feature ? "feature" : "input", 349225839Smav use_rid ? dbuf[0] : 0, dlen); 350225839Smav if (havedata) { 351225839Smav for (i = 0; i < dlen; i++) 352225839Smav printf(" %02x", dbuf[i]); 353225839Smav } 354225839Smav printf("\n"); 35562642Sn_hibma } 356225839Smav match = 0; 357225839Smav for (var = vars; var; var = var->next) { 358225839Smav if ((kind < 3 ? kind : hid_input) != var->h.kind) 359224511Smav continue; 360225839Smav if (var->h.report_ID != 0 && 361225839Smav dbuf[0] != var->h.report_ID) 362225839Smav continue; 363225839Smav match = 1; 364225839Smav if (!noname) 365225839Smav printf("%s=", var->name); 366225839Smav if (havedata) 367225839Smav prdata(dbuf, &var->h); 368225839Smav printf("\n"); 369225839Smav } 370225839Smav if (match) 371225839Smav printf("\n"); 372225839Smav free(dbuf); 373225839Smav } while (loop || kind < 3); 374225839Smav} 375225839Smav 376225839Smavstatic void 377225839Smavwritedata(int f, report_desc_t rd) 378225839Smav{ 379225839Smav struct variable *var; 380225839Smav int dlen, i, r, rid; 381225839Smav u_char *dbuf; 382225839Smav enum hid_kind kind; 383225839Smav 384225839Smav kind = 0; 385225839Smav rid = 0; 386225839Smav for (kind = 0; kind < 3; kind ++) { 387225839Smav for (rid = 0; rid < 256; rid ++) { 388225839Smav for (var = vars; var; var = var->next) { 389225839Smav if (rid == var->h.report_ID && kind == var->h.kind) 390225839Smav break; 391225839Smav } 392225839Smav if (var == NULL) 393225839Smav continue; 394225839Smav dlen = hid_report_size(rd, kind, rid); 395225839Smav if (dlen <= 0) 396225839Smav continue; 397225839Smav dbuf = malloc(dlen); 398225839Smav memset(dbuf, 0, dlen); 399225839Smav dbuf[0] = rid; 400225839Smav if (!zflag && hid_get_report(f, kind, dbuf, dlen) == 0) { 401225839Smav if (verbose) { 402225839Smav printf("Got %s report %d (%d bytes):", 403225839Smav kind == hid_input ? "input" : 404225839Smav kind == hid_output ? "output" : "feature", 405225839Smav rid, dlen); 406225839Smav for (i = 0; i < dlen; i++) 407225839Smav printf(" %02x", dbuf[i]); 40862642Sn_hibma printf("\n"); 40962642Sn_hibma } 410225839Smav } else if (!zflag) { 411225839Smav warn("hid_get_report(rid %d)", rid); 412225839Smav if (verbose) { 413225839Smav printf("Can't get %s report %d (%d bytes). " 414225839Smav "Will be initialized with zeros.\n", 415225839Smav kind == hid_input ? "input" : 416225839Smav kind == hid_output ? "output" : "feature", 417225839Smav rid, dlen); 418225839Smav } 41962642Sn_hibma } 420225839Smav for (var = vars; var; var = var->next) { 421225839Smav if (rid != var->h.report_ID || kind != var->h.kind) 422225839Smav continue; 423225839Smav hid_set_data(dbuf, &var->h, var->val); 424225839Smav } 425225839Smav if (verbose) { 426225839Smav printf("Setting %s report %d (%d bytes):", 427225839Smav kind == hid_output ? "output" : 428225839Smav kind == hid_feature ? "feature" : "input", 429225839Smav rid, dlen); 430225839Smav for (i = 0; i < dlen; i++) 431225839Smav printf(" %02x", dbuf[i]); 43262642Sn_hibma printf("\n"); 433225839Smav } 434225839Smav r = hid_set_report(f, kind, dbuf, dlen); 435225839Smav if (r != 0) 436225839Smav warn("hid_set_report(rid %d)", rid); 437225839Smav free(dbuf); 438225839Smav } 439225839Smav } 44062642Sn_hibma} 44162642Sn_hibma 44262642Sn_hibmaint 44362642Sn_hibmamain(int argc, char **argv) 44462642Sn_hibma{ 445225839Smav report_desc_t r; 446225839Smav char *table = 0; 447225839Smav char devnam[100], *dev = NULL; 44862642Sn_hibma int f; 449225839Smav int all = 0; 45062642Sn_hibma int ch; 45162642Sn_hibma int repdump = 0; 45262642Sn_hibma int loop = 0; 45362642Sn_hibma 454225839Smav while ((ch = getopt(argc, argv, "af:lnrt:vwxz")) != -1) { 45562642Sn_hibma switch(ch) { 45662642Sn_hibma case 'a': 45762642Sn_hibma all++; 45862642Sn_hibma break; 45962642Sn_hibma case 'f': 46062642Sn_hibma dev = optarg; 46162642Sn_hibma break; 46262642Sn_hibma case 'l': 46362642Sn_hibma loop ^= 1; 46462642Sn_hibma break; 46562642Sn_hibma case 'n': 46662642Sn_hibma noname++; 46762642Sn_hibma break; 46862642Sn_hibma case 'r': 46962642Sn_hibma repdump++; 47062642Sn_hibma break; 47162642Sn_hibma case 't': 47262642Sn_hibma table = optarg; 47362642Sn_hibma break; 47462642Sn_hibma case 'v': 47562642Sn_hibma verbose++; 47662642Sn_hibma break; 477225839Smav case 'w': 478225839Smav wflag = 1; 479225839Smav break; 480164531Sgrog case 'x': 481164544Sgrog hexdump = 1; 482164531Sgrog break; 483225839Smav case 'z': 484225839Smav zflag = 1; 485225839Smav break; 48662642Sn_hibma case '?': 48762642Sn_hibma default: 48862642Sn_hibma usage(); 48962642Sn_hibma } 49062642Sn_hibma } 49162642Sn_hibma argc -= optind; 49262642Sn_hibma argv += optind; 493225839Smav if (dev == NULL) 49462642Sn_hibma usage(); 49562642Sn_hibma 496225839Smav if (argc == 0 && !all && !repdump) 49762642Sn_hibma usage(); 49862642Sn_hibma 49962642Sn_hibma if (dev[0] != '/') { 50062642Sn_hibma if (isdigit(dev[0])) 50187699Smarkm snprintf(devnam, sizeof(devnam), "/dev/uhid%s", dev); 50262642Sn_hibma else 50387699Smarkm snprintf(devnam, sizeof(devnam), "/dev/%s", dev); 50487699Smarkm dev = devnam; 50562642Sn_hibma } 50662642Sn_hibma 50762642Sn_hibma hid_init(table); 50862642Sn_hibma 50962642Sn_hibma f = open(dev, O_RDWR); 51062642Sn_hibma if (f < 0) 51162642Sn_hibma err(1, "%s", dev); 51262642Sn_hibma 51362642Sn_hibma r = hid_get_report_desc(f); 514164531Sgrog if (r == 0) 51562642Sn_hibma errx(1, "USB_GET_REPORT_DESC"); 516164531Sgrog 51762642Sn_hibma if (repdump) { 51862642Sn_hibma printf("Report descriptor:\n"); 51962642Sn_hibma dumpitems(r); 52062642Sn_hibma } 521225839Smav if (argc != 0 || all) { 522225839Smav parceargs(r, all, argc, argv); 523225839Smav if (wflag) 524225839Smav writedata(f, r); 525225839Smav else 526225839Smav dumpdata(f, r, loop); 527225839Smav } 52862642Sn_hibma 52962642Sn_hibma hid_dispose_report_desc(r); 53062642Sn_hibma exit(0); 53162642Sn_hibma} 532