]>
Commit | Line | Data |
---|---|---|
65561351 GH |
1 | /* |
2 | * USB Mass Storage Device emulation | |
3 | * | |
4 | * Copyright (c) 2006 CodeSourcery. | |
5 | * Written by Paul Brook | |
6 | * | |
7 | * This code is licensed under the LGPL. | |
8 | */ | |
9 | ||
10 | #include "qemu/osdep.h" | |
11 | #include "qemu/typedefs.h" | |
12 | #include "qapi/error.h" | |
13 | #include "qapi/visitor.h" | |
14 | #include "hw/usb.h" | |
15 | #include "hw/usb/desc.h" | |
16 | #include "hw/usb/msd.h" | |
17 | #include "sysemu/sysemu.h" | |
18 | #include "sysemu/block-backend.h" | |
19 | ||
20 | static const struct SCSIBusInfo usb_msd_scsi_info_storage = { | |
21 | .tcq = false, | |
22 | .max_target = 0, | |
23 | .max_lun = 0, | |
24 | ||
25 | .transfer_data = usb_msd_transfer_data, | |
26 | .complete = usb_msd_command_complete, | |
27 | .cancel = usb_msd_request_cancelled, | |
28 | .load_request = usb_msd_load_request, | |
29 | }; | |
30 | ||
31 | static void usb_msd_storage_realize(USBDevice *dev, Error **errp) | |
32 | { | |
33 | MSDState *s = USB_STORAGE_DEV(dev); | |
34 | BlockBackend *blk = s->conf.blk; | |
35 | SCSIDevice *scsi_dev; | |
36 | ||
37 | if (!blk) { | |
38 | error_setg(errp, "drive property not set"); | |
39 | return; | |
40 | } | |
41 | ||
42 | if (!blkconf_blocksizes(&s->conf, errp)) { | |
43 | return; | |
44 | } | |
45 | ||
46 | if (!blkconf_apply_backend_options(&s->conf, !blk_supports_write_perm(blk), | |
47 | true, errp)) { | |
48 | return; | |
49 | } | |
50 | ||
51 | /* | |
52 | * Hack alert: this pretends to be a block device, but it's really | |
53 | * a SCSI bus that can serve only a single device, which it | |
54 | * creates automatically. But first it needs to detach from its | |
55 | * blockdev, or else scsi_bus_legacy_add_drive() dies when it | |
56 | * attaches again. We also need to take another reference so that | |
57 | * blk_detach_dev() doesn't free blk while we still need it. | |
58 | * | |
59 | * The hack is probably a bad idea. | |
60 | */ | |
61 | blk_ref(blk); | |
62 | blk_detach_dev(blk, DEVICE(s)); | |
63 | s->conf.blk = NULL; | |
64 | ||
65 | usb_desc_create_serial(dev); | |
66 | usb_desc_init(dev); | |
67 | scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev), | |
68 | &usb_msd_scsi_info_storage, NULL); | |
69 | scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, | |
70 | s->conf.bootindex, s->conf.share_rw, | |
71 | s->conf.rerror, s->conf.werror, | |
72 | dev->serial, | |
73 | errp); | |
74 | blk_unref(blk); | |
75 | if (!scsi_dev) { | |
76 | return; | |
77 | } | |
78 | usb_msd_handle_reset(dev); | |
79 | s->scsi_dev = scsi_dev; | |
80 | } | |
81 | ||
82 | static Property msd_properties[] = { | |
83 | DEFINE_BLOCK_PROPERTIES(MSDState, conf), | |
84 | DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), | |
85 | DEFINE_PROP_BOOL("removable", MSDState, removable, false), | |
86 | DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false), | |
87 | DEFINE_PROP_END_OF_LIST(), | |
88 | }; | |
89 | ||
90 | static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) | |
91 | { | |
92 | DeviceClass *dc = DEVICE_CLASS(klass); | |
93 | USBDeviceClass *uc = USB_DEVICE_CLASS(klass); | |
94 | ||
95 | uc->realize = usb_msd_storage_realize; | |
96 | device_class_set_props(dc, msd_properties); | |
97 | } | |
98 | ||
99 | static void usb_msd_get_bootindex(Object *obj, Visitor *v, const char *name, | |
100 | void *opaque, Error **errp) | |
101 | { | |
102 | USBDevice *dev = USB_DEVICE(obj); | |
103 | MSDState *s = USB_STORAGE_DEV(dev); | |
104 | ||
105 | visit_type_int32(v, name, &s->conf.bootindex, errp); | |
106 | } | |
107 | ||
108 | static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name, | |
109 | void *opaque, Error **errp) | |
110 | { | |
111 | USBDevice *dev = USB_DEVICE(obj); | |
112 | MSDState *s = USB_STORAGE_DEV(dev); | |
113 | int32_t boot_index; | |
114 | Error *local_err = NULL; | |
115 | ||
116 | if (!visit_type_int32(v, name, &boot_index, errp)) { | |
117 | return; | |
118 | } | |
119 | /* check whether bootindex is present in fw_boot_order list */ | |
120 | check_boot_index(boot_index, &local_err); | |
121 | if (local_err) { | |
122 | goto out; | |
123 | } | |
124 | /* change bootindex to a new one */ | |
125 | s->conf.bootindex = boot_index; | |
126 | ||
127 | if (s->scsi_dev) { | |
128 | object_property_set_int(OBJECT(s->scsi_dev), "bootindex", boot_index, | |
129 | &error_abort); | |
130 | } | |
131 | ||
132 | out: | |
133 | error_propagate(errp, local_err); | |
134 | } | |
135 | ||
136 | static void usb_msd_instance_init(Object *obj) | |
137 | { | |
138 | object_property_add(obj, "bootindex", "int32", | |
139 | usb_msd_get_bootindex, | |
140 | usb_msd_set_bootindex, NULL, NULL); | |
141 | object_property_set_int(obj, "bootindex", -1, NULL); | |
142 | } | |
143 | ||
144 | static const TypeInfo msd_info = { | |
145 | .name = "usb-storage", | |
146 | .parent = TYPE_USB_STORAGE, | |
147 | .class_init = usb_msd_class_storage_initfn, | |
148 | .instance_init = usb_msd_instance_init, | |
149 | }; | |
150 | ||
151 | static void register_types(void) | |
152 | { | |
153 | type_register_static(&msd_info); | |
154 | } | |
155 | ||
156 | type_init(register_types) |