Merge branch 'master' of /home/nchip/public_html/qemu into garage-push
[qemu] / hw / scsi-generic.c
1 /*
2  * Generic SCSI Device support
3  *
4  * Copyright (c) 2007 Bull S.A.S.
5  * Based on code by Paul Brook
6  * Based on code by Fabrice Bellard
7  *
8  * Written by Laurent Vivier <Laurent.Vivier@bull.net>
9  *
10  * This code is licenced under the LGPL.
11  *
12  */
13
14 #include "qemu-common.h"
15 #include "block.h"
16 #include "scsi-disk.h"
17
18 #ifndef __linux__
19
20 SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
21                               scsi_completionfn completion, void *opaque)
22 {
23     return NULL;
24 }
25
26 #else /* __linux__ */
27
28 //#define DEBUG_SCSI
29
30 #ifdef DEBUG_SCSI
31 #define DPRINTF(fmt, ...) \
32 do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
33 #else
34 #define DPRINTF(fmt, ...) do {} while(0)
35 #endif
36
37 #define BADF(fmt, ...) \
38 do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
39
40 #include <stdio.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <scsi/sg.h>
45 #include <scsi/scsi.h>
46
47 #define REWIND 0x01
48 #define REPORT_DENSITY_SUPPORT 0x44
49 #define LOAD_UNLOAD 0xa6
50 #define SET_CD_SPEED 0xbb
51 #define BLANK 0xa1
52
53 #define SCSI_CMD_BUF_SIZE     16
54 #define SCSI_SENSE_BUF_SIZE 96
55
56 #define SG_ERR_DRIVER_TIMEOUT 0x06
57 #define SG_ERR_DRIVER_SENSE 0x08
58
59 #ifndef MAX_UINT
60 #define MAX_UINT ((unsigned int)-1)
61 #endif
62
63 typedef struct SCSIRequest {
64     BlockDriverAIOCB *aiocb;
65     struct SCSIRequest *next;
66     SCSIDeviceState *dev;
67     uint32_t tag;
68     uint8_t cmd[SCSI_CMD_BUF_SIZE];
69     int cmdlen;
70     uint8_t *buf;
71     int buflen;
72     int len;
73     sg_io_hdr_t io_header;
74 } SCSIRequest;
75
76 struct SCSIDeviceState
77 {
78     SCSIRequest *requests;
79     BlockDriverState *bdrv;
80     int type;
81     int blocksize;
82     int lun;
83     scsi_completionfn completion;
84     void *opaque;
85     int driver_status;
86     uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
87     uint8_t senselen;
88 };
89
90 /* Global pool of SCSIRequest structures.  */
91 static SCSIRequest *free_requests = NULL;
92
93 static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
94 {
95     SCSIRequest *r;
96
97     if (free_requests) {
98         r = free_requests;
99         free_requests = r->next;
100     } else {
101         r = qemu_malloc(sizeof(SCSIRequest));
102         r->buf = NULL;
103         r->buflen = 0;
104     }
105     r->dev = s;
106     r->tag = tag;
107     memset(r->cmd, 0, sizeof(r->cmd));
108     memset(&r->io_header, 0, sizeof(r->io_header));
109     r->cmdlen = 0;
110     r->len = 0;
111     r->aiocb = NULL;
112
113     /* link */
114
115     r->next = s->requests;
116     s->requests = r;
117     return r;
118 }
119
120 static void scsi_remove_request(SCSIRequest *r)
121 {
122     SCSIRequest *last;
123     SCSIDeviceState *s = r->dev;
124
125     if (s->requests == r) {
126         s->requests = r->next;
127     } else {
128         last = s->requests;
129         while (last && last->next != r)
130             last = last->next;
131         if (last) {
132             last->next = r->next;
133         } else {
134             BADF("Orphaned request\n");
135         }
136     }
137     r->next = free_requests;
138     free_requests = r;
139 }
140
141 static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
142 {
143     SCSIRequest *r;
144
145     r = s->requests;
146     while (r && r->tag != tag)
147         r = r->next;
148
149     return r;
150 }
151
152 /* Helper function for command completion.  */
153 static void scsi_command_complete(void *opaque, int ret)
154 {
155     SCSIRequest *r = (SCSIRequest *)opaque;
156     SCSIDeviceState *s = r->dev;
157     uint32_t tag;
158     int status;
159
160     s->driver_status = r->io_header.driver_status;
161     if (s->driver_status & SG_ERR_DRIVER_SENSE)
162         s->senselen = r->io_header.sb_len_wr;
163
164     if (ret != 0)
165         status = BUSY << 1;
166     else {
167         if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
168             status = BUSY << 1;
169             BADF("Driver Timeout\n");
170         } else if (r->io_header.status)
171             status = r->io_header.status;
172         else if (s->driver_status & SG_ERR_DRIVER_SENSE)
173             status = CHECK_CONDITION << 1;
174         else
175             status = GOOD << 1;
176     }
177     DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
178             r, r->tag, status);
179     tag = r->tag;
180     scsi_remove_request(r);
181     s->completion(s->opaque, SCSI_REASON_DONE, tag, status);
182 }
183
184 /* Cancel a pending data transfer.  */
185 static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
186 {
187     DPRINTF("scsi_cancel_io 0x%x\n", tag);
188     SCSIDeviceState *s = d->state;
189     SCSIRequest *r;
190     DPRINTF("Cancel tag=0x%x\n", tag);
191     r = scsi_find_request(s, tag);
192     if (r) {
193         if (r->aiocb)
194             bdrv_aio_cancel(r->aiocb);
195         r->aiocb = NULL;
196         scsi_remove_request(r);
197     }
198 }
199
200 static int execute_command(BlockDriverState *bdrv,
201                            SCSIRequest *r, int direction,
202                            BlockDriverCompletionFunc *complete)
203 {
204     r->io_header.interface_id = 'S';
205     r->io_header.dxfer_direction = direction;
206     r->io_header.dxferp = r->buf;
207     r->io_header.dxfer_len = r->buflen;
208     r->io_header.cmdp = r->cmd;
209     r->io_header.cmd_len = r->cmdlen;
210     r->io_header.mx_sb_len = sizeof(r->dev->sensebuf);
211     r->io_header.sbp = r->dev->sensebuf;
212     r->io_header.timeout = MAX_UINT;
213     r->io_header.usr_ptr = r;
214     r->io_header.flags |= SG_FLAG_DIRECT_IO;
215
216     r->aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
217     if (r->aiocb == NULL) {
218         BADF("execute_command: read failed !\n");
219         return -1;
220     }
221
222     return 0;
223 }
224
225 static void scsi_read_complete(void * opaque, int ret)
226 {
227     SCSIRequest *r = (SCSIRequest *)opaque;
228     SCSIDeviceState *s = r->dev;
229     int len;
230
231     if (ret) {
232         DPRINTF("IO error\n");
233         scsi_command_complete(r, ret);
234         return;
235     }
236     len = r->io_header.dxfer_len - r->io_header.resid;
237     DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len);
238
239     r->len = -1;
240     s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
241     if (len == 0)
242         scsi_command_complete(r, 0);
243 }
244
245 /* Read more data from scsi device into buffer.  */
246 static void scsi_read_data(SCSIDevice *d, uint32_t tag)
247 {
248     SCSIDeviceState *s = d->state;
249     SCSIRequest *r;
250     int ret;
251
252     DPRINTF("scsi_read_data 0x%x\n", tag);
253     r = scsi_find_request(s, tag);
254     if (!r) {
255         BADF("Bad read tag 0x%x\n", tag);
256         /* ??? This is the wrong error.  */
257         scsi_command_complete(r, -EINVAL);
258         return;
259     }
260
261     if (r->len == -1) {
262         scsi_command_complete(r, 0);
263         return;
264     }
265
266     if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
267     {
268         s->senselen = MIN(r->len, s->senselen);
269         memcpy(r->buf, s->sensebuf, s->senselen);
270         r->io_header.driver_status = 0;
271         r->io_header.status = 0;
272         r->io_header.dxfer_len  = s->senselen;
273         r->len = -1;
274         DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, s->senselen);
275         DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
276                 r->buf[0], r->buf[1], r->buf[2], r->buf[3],
277                 r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
278         s->completion(s->opaque, SCSI_REASON_DATA, r->tag, s->senselen);
279         return;
280     }
281
282     ret = execute_command(s->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete);
283     if (ret == -1) {
284         scsi_command_complete(r, -EINVAL);
285         return;
286     }
287 }
288
289 static void scsi_write_complete(void * opaque, int ret)
290 {
291     SCSIRequest *r = (SCSIRequest *)opaque;
292
293     DPRINTF("scsi_write_complete() ret = %d\n", ret);
294     if (ret) {
295         DPRINTF("IO error\n");
296         scsi_command_complete(r, ret);
297         return;
298     }
299
300     if (r->cmd[0] == MODE_SELECT && r->cmd[4] == 12 &&
301         r->dev->type == TYPE_TAPE) {
302         r->dev->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
303         DPRINTF("block size %d\n", r->dev->blocksize);
304     }
305
306     scsi_command_complete(r, ret);
307 }
308
309 /* Write data to a scsi device.  Returns nonzero on failure.
310    The transfer may complete asynchronously.  */
311 static int scsi_write_data(SCSIDevice *d, uint32_t tag)
312 {
313     SCSIDeviceState *s = d->state;
314     SCSIRequest *r;
315     int ret;
316
317     DPRINTF("scsi_write_data 0x%x\n", tag);
318     r = scsi_find_request(s, tag);
319     if (!r) {
320         BADF("Bad write tag 0x%x\n", tag);
321         /* ??? This is the wrong error.  */
322         scsi_command_complete(r, -EINVAL);
323         return 0;
324     }
325
326     if (r->len == 0) {
327         r->len = r->buflen;
328         s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->len);
329         return 0;
330     }
331
332     ret = execute_command(s->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete);
333     if (ret == -1) {
334         scsi_command_complete(r, -EINVAL);
335         return 1;
336     }
337
338     return 0;
339 }
340
341 /* Return a pointer to the data buffer.  */
342 static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
343 {
344     SCSIDeviceState *s = d->state;
345     SCSIRequest *r;
346     r = scsi_find_request(s, tag);
347     if (!r) {
348         BADF("Bad buffer tag 0x%x\n", tag);
349         return NULL;
350     }
351     return r->buf;
352 }
353
354 static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len)
355 {
356     switch (cmd[0] >> 5) {
357     case 0:
358         *len = cmd[4];
359         *cmdlen = 6;
360         /* length 0 means 256 blocks */
361         if (*len == 0)
362             *len = 256;
363         break;
364     case 1:
365     case 2:
366         *len = cmd[8] | (cmd[7] << 8);
367         *cmdlen = 10;
368         break;
369     case 4:
370         *len = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24);
371         *cmdlen = 16;
372         break;
373     case 5:
374         *len = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24);
375         *cmdlen = 12;
376         break;
377     default:
378         return -1;
379     }
380
381     switch(cmd[0]) {
382     case TEST_UNIT_READY:
383     case REZERO_UNIT:
384     case START_STOP:
385     case SEEK_6:
386     case WRITE_FILEMARKS:
387     case SPACE:
388     case ERASE:
389     case ALLOW_MEDIUM_REMOVAL:
390     case VERIFY:
391     case SEEK_10:
392     case SYNCHRONIZE_CACHE:
393     case LOCK_UNLOCK_CACHE:
394     case LOAD_UNLOAD:
395     case SET_CD_SPEED:
396     case SET_LIMITS:
397     case WRITE_LONG:
398     case MOVE_MEDIUM:
399     case UPDATE_BLOCK:
400         *len = 0;
401         break;
402     case MODE_SENSE:
403         break;
404     case WRITE_SAME:
405         *len = 1;
406         break;
407     case READ_CAPACITY:
408         *len = 8;
409         break;
410     case READ_BLOCK_LIMITS:
411         *len = 6;
412         break;
413     case READ_POSITION:
414         *len = 20;
415         break;
416     case SEND_VOLUME_TAG:
417         *len *= 40;
418         break;
419     case MEDIUM_SCAN:
420         *len *= 8;
421         break;
422     case WRITE_10:
423         cmd[1] &= ~0x08;        /* disable FUA */
424     case WRITE_VERIFY:
425     case WRITE_6:
426     case WRITE_12:
427     case WRITE_VERIFY_12:
428         *len *= blocksize;
429         break;
430     case READ_10:
431         cmd[1] &= ~0x08;        /* disable FUA */
432     case READ_6:
433     case READ_REVERSE:
434     case RECOVER_BUFFERED_DATA:
435     case READ_12:
436         *len *= blocksize;
437         break;
438     case INQUIRY:
439         *len = cmd[4] | (cmd[3] << 8);
440         break;
441     }
442     return 0;
443 }
444
445 static int scsi_stream_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len)
446 {
447     switch(cmd[0]) {
448     /* stream commands */
449     case READ_6:
450     case READ_REVERSE:
451     case RECOVER_BUFFERED_DATA:
452     case WRITE_6:
453         *cmdlen = 6;
454         *len = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16);
455         if (cmd[1] & 0x01) /* fixed */
456             *len *= blocksize;
457         break;
458     case REWIND:
459     case START_STOP:
460         *cmdlen = 6;
461         *len = 0;
462         cmd[1] = 0x01;  /* force IMMED, otherwise qemu waits end of command */
463         break;
464     /* generic commands */
465     default:
466         return scsi_length(cmd, blocksize, cmdlen, len);
467     }
468     return 0;
469 }
470
471 static int is_write(int command)
472 {
473     switch (command) {
474     case COPY:
475     case COPY_VERIFY:
476     case COMPARE:
477     case CHANGE_DEFINITION:
478     case LOG_SELECT:
479     case MODE_SELECT:
480     case MODE_SELECT_10:
481     case SEND_DIAGNOSTIC:
482     case WRITE_BUFFER:
483     case FORMAT_UNIT:
484     case REASSIGN_BLOCKS:
485     case RESERVE:
486     case SEARCH_EQUAL:
487     case SEARCH_HIGH:
488     case SEARCH_LOW:
489     case WRITE_6:
490     case WRITE_10:
491     case WRITE_VERIFY:
492     case UPDATE_BLOCK:
493     case WRITE_LONG:
494     case WRITE_SAME:
495     case SEARCH_HIGH_12:
496     case SEARCH_EQUAL_12:
497     case SEARCH_LOW_12:
498     case WRITE_12:
499     case WRITE_VERIFY_12:
500     case SET_WINDOW:
501     case MEDIUM_SCAN:
502     case SEND_VOLUME_TAG:
503     case WRITE_LONG_2:
504         return 1;
505     }
506     return 0;
507 }
508
509 /* Execute a scsi command.  Returns the length of the data expected by the
510    command.  This will be Positive for data transfers from the device
511    (eg. disk reads), negative for transfers to the device (eg. disk writes),
512    and zero if the command does not transfer any data.  */
513
514 static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
515                                  uint8_t *cmd, int lun)
516 {
517     SCSIDeviceState *s = d->state;
518     uint32_t len=0;
519     int cmdlen=0;
520     SCSIRequest *r;
521     int ret;
522
523     if (s->type == TYPE_TAPE) {
524         if (scsi_stream_length(cmd, s->blocksize, &cmdlen, &len) == -1) {
525             BADF("Unsupported command length, command %x\n", cmd[0]);
526             return 0;
527         }
528      } else {
529         if (scsi_length(cmd, s->blocksize, &cmdlen, &len) == -1) {
530             BADF("Unsupported command length, command %x\n", cmd[0]);
531             return 0;
532         }
533     }
534
535     DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag,
536             cmd[0], len);
537
538     if (cmd[0] != REQUEST_SENSE &&
539         (lun != s->lun || (cmd[1] >> 5) != s->lun)) {
540         DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
541
542         s->sensebuf[0] = 0x70;
543         s->sensebuf[1] = 0x00;
544         s->sensebuf[2] = ILLEGAL_REQUEST;
545         s->sensebuf[3] = 0x00;
546         s->sensebuf[4] = 0x00;
547         s->sensebuf[5] = 0x00;
548         s->sensebuf[6] = 0x00;
549         s->senselen = 7;
550         s->driver_status = SG_ERR_DRIVER_SENSE;
551         s->completion(s->opaque, SCSI_REASON_DONE, tag, CHECK_CONDITION << 1);
552         return 0;
553     }
554
555     r = scsi_find_request(s, tag);
556     if (r) {
557         BADF("Tag 0x%x already in use %p\n", tag, r);
558         scsi_cancel_io(d, tag);
559     }
560     r = scsi_new_request(s, tag);
561
562     memcpy(r->cmd, cmd, cmdlen);
563     r->cmdlen = cmdlen;
564
565     if (len == 0) {
566         if (r->buf != NULL)
567             free(r->buf);
568         r->buflen = 0;
569         r->buf = NULL;
570         ret = execute_command(s->bdrv, r, SG_DXFER_NONE, scsi_command_complete);
571         if (ret == -1) {
572             scsi_command_complete(r, -EINVAL);
573             return 0;
574         }
575         return 0;
576     }
577
578     if (r->buflen != len) {
579         if (r->buf != NULL)
580             free(r->buf);
581         r->buf = qemu_malloc(len);
582         r->buflen = len;
583     }
584
585     memset(r->buf, 0, r->buflen);
586     r->len = len;
587     if (is_write(cmd[0])) {
588         r->len = 0;
589         return -len;
590     }
591
592     return len;
593 }
594
595 static int get_blocksize(BlockDriverState *bdrv)
596 {
597     uint8_t cmd[10];
598     uint8_t buf[8];
599     uint8_t sensebuf[8];
600     sg_io_hdr_t io_header;
601     int ret;
602
603     memset(cmd, 0, sizeof(cmd));
604     memset(buf, 0, sizeof(buf));
605     cmd[0] = READ_CAPACITY;
606
607     memset(&io_header, 0, sizeof(io_header));
608     io_header.interface_id = 'S';
609     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
610     io_header.dxfer_len = sizeof(buf);
611     io_header.dxferp = buf;
612     io_header.cmdp = cmd;
613     io_header.cmd_len = sizeof(cmd);
614     io_header.mx_sb_len = sizeof(sensebuf);
615     io_header.sbp = sensebuf;
616     io_header.timeout = 6000; /* XXX */
617
618     ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
619     if (ret < 0)
620         return -1;
621
622     return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
623 }
624
625 static int get_stream_blocksize(BlockDriverState *bdrv)
626 {
627     uint8_t cmd[6];
628     uint8_t buf[12];
629     uint8_t sensebuf[8];
630     sg_io_hdr_t io_header;
631     int ret;
632
633     memset(cmd, 0, sizeof(cmd));
634     memset(buf, 0, sizeof(buf));
635     cmd[0] = MODE_SENSE;
636     cmd[4] = sizeof(buf);
637
638     memset(&io_header, 0, sizeof(io_header));
639     io_header.interface_id = 'S';
640     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
641     io_header.dxfer_len = sizeof(buf);
642     io_header.dxferp = buf;
643     io_header.cmdp = cmd;
644     io_header.cmd_len = sizeof(cmd);
645     io_header.mx_sb_len = sizeof(sensebuf);
646     io_header.sbp = sensebuf;
647     io_header.timeout = 6000; /* XXX */
648
649     ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
650     if (ret < 0)
651         return -1;
652
653     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
654 }
655
656 static void scsi_destroy(SCSIDevice *d)
657 {
658     SCSIRequest *r, *n;
659
660     r = d->state->requests;
661     while (r) {
662         n = r->next;
663         qemu_free(r);
664         r = n;
665     }
666
667     r = free_requests;
668     while (r) {
669         n = r->next;
670         qemu_free(r);
671         r = n;
672     }
673
674     qemu_free(d->state);
675     qemu_free(d);
676 }
677
678 SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
679                               scsi_completionfn completion, void *opaque)
680 {
681     int sg_version;
682     SCSIDevice *d;
683     SCSIDeviceState *s;
684     struct sg_scsi_id scsiid;
685
686     /* check we are really using a /dev/sg* file */
687
688     if (!bdrv_is_sg(bdrv))
689         return NULL;
690
691     /* check we are using a driver managing SG_IO (version 3 and after */
692
693     if (bdrv_ioctl(bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 ||
694         sg_version < 30000)
695         return NULL;
696
697     /* get LUN of the /dev/sg? */
698
699     if (bdrv_ioctl(bdrv, SG_GET_SCSI_ID, &scsiid))
700         return NULL;
701
702     /* define device state */
703
704     s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
705     s->bdrv = bdrv;
706     s->requests = NULL;
707     s->completion = completion;
708     s->opaque = opaque;
709     s->lun = scsiid.lun;
710     DPRINTF("LUN %d\n", s->lun);
711     s->type = scsiid.scsi_type;
712     DPRINTF("device type %d\n", s->type);
713     if (s->type == TYPE_TAPE) {
714         s->blocksize = get_stream_blocksize(s->bdrv);
715         if (s->blocksize == -1)
716             s->blocksize = 0;
717     } else {
718         s->blocksize = get_blocksize(s->bdrv);
719         /* removable media returns 0 if not present */
720         if (s->blocksize <= 0) {
721             if (s->type == TYPE_ROM || s->type  == TYPE_WORM)
722                 s->blocksize = 2048;
723             else
724                 s->blocksize = 512;
725         }
726     }
727     DPRINTF("block size %d\n", s->blocksize);
728     s->driver_status = 0;
729     memset(s->sensebuf, 0, sizeof(s->sensebuf));
730
731     /* define function to manage device */
732
733     d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
734     d->state = s;
735     d->destroy = scsi_destroy;
736     d->send_command = scsi_send_command;
737     d->read_data = scsi_read_data;
738     d->write_data = scsi_write_data;
739     d->cancel_io = scsi_cancel_io;
740     d->get_buf = scsi_get_buf;
741
742     return d;
743 }
744 #endif /* __linux__ */