OLD | NEW |
---|---|
1 <h1>Accessing Hardware Devices</h1> | 1 <h1>Accessing Hardware Devices</h1> |
2 | 2 |
3 <p> | 3 <p> |
4 This doc shows you how packaged apps can connect to USB devices | 4 This doc shows you how packaged apps can connect to USB devices and read from |
5 and read from and write to a user's serial ports. | 5 and write to a user's serial ports. See also the reference docs for the <a |
6 See also the reference docs for the | 6 href="usb.html">USB API</a> and the <a href="serial.html">Serial API</a>. |
7 <a href="usb.html">USB API</a> | 7 The <a href="bluetooth.html">Bluetooth API</a> is also available; we've |
8 and the | 8 included a link to a Bluetooth sample below. |
9 <a href="serial.html">Serial API</a>. | |
10 The <a href="bluetooth.html">Bluetooth API</a> is also available; | |
11 we've include a link to a Bluetooth sample below. | |
12 </p> | 9 </p> |
13 | 10 |
14 <p class="note"> | 11 <p class="note"> |
15 <b>API Samples: </b> | 12 <b>API Samples: </b> Want to play with the code? Check out the |
16 Want to play with the code? | 13 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/serial ">serial</a>, |
17 Check out the | 14 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/servo" >servo</a>, |
18 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/serial"> serial</a>, | 15 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/usb">u sb</a>, |
19 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/servo">s ervo</a>, | 16 and |
20 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/usb">usb </a>, | 17 <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/zephyr _hxm">zephyr_hxm |
21 and <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/zeph yr_hxm">zephyr_hxm Bluetooth</a> samples. | 18 Bluetooth</a> samples. |
22 </p> | 19 </p> |
23 | 20 |
24 <h2 id="usb">Accessing USB devices</h2> | 21 <h2 id="usb">Accessing USB devices</h2> |
25 | 22 |
26 <p> | 23 <p> |
27 You can use the USB API to send messages to connected devices using only JavaScr ipt code. Some devices are not accessible through this API - see the <a href="#c aveats">Caveats section</a> below for more details. | 24 You can use the <a href="usb.html">USB API</a> to communicate with USB devices |
25 using only JavaScript code. Some devices are not accessible through this API - | |
Andy
2013/09/06 23:15:01
The convention is to separate asides with a long d
Bei Zhang
2013/09/09 22:02:34
Done.
| |
26 see <a href="#caveats">Caveats</a> below for more details. | |
Andy
2013/09/06 23:15:01
Delete "more". You haven't provided any details s
Bei Zhang
2013/09/09 22:02:34
Done.
| |
27 </p> | |
28 | |
29 <p> | |
30 For background information about USB, see the official | |
31 <a href="http://www.usb.org/home">USB specifications</a>. <br/> | |
32 <a href="http://www.beyondlogic.org/usbnutshell/usb1.shtml"> | |
33 <i>USB in a NutShell</i></a> | |
34 is a reasonable crash course about it (you may be able to finish it within 24 | |
Andy
2013/09/06 23:15:01
1) Delete the parenthetical "(you may be able... )
Bei Zhang
2013/09/09 22:02:34
Done.
| |
35 hours). | |
28 </p> | 36 </p> |
29 | 37 |
30 <h3 id="manifest">Manifest requirement</h3> | 38 <h3 id="manifest">Manifest requirement</h3> |
31 | 39 |
32 <p> | 40 <p>The USB API requires the "usb" permission in the manifest file:</p> |
33 The USB API requires a special permission "usb" in the manifest file: | |
34 </p> | |
35 | 41 |
36 <pre> | 42 <pre> |
37 "permissions": [ | 43 "permissions": [ |
38 "usb" | 44 "usb" |
39 ] | 45 ] |
40 </pre> | 46 </pre> |
41 | 47 |
48 <p>In order to prevent | |
Andy
2013/09/06 23:15:01
Start this paragraph with a transition: "In additi
Bei Zhang
2013/09/09 22:02:34
Done.
| |
49 <a href="http://en.wikipedia.org/wiki/Device_fingerprint">finger-printing</a>, | |
50 you must declare all the device type you want to access in the manifest file. | |
Andy
2013/09/06 23:15:01
"type" -> "types" (plural)
Bei Zhang
2013/09/09 22:02:34
Done.
| |
51 Each type of USB device corresponds to a vendor id/product id (VID/PID) pair. | |
52 You can use $ref:usb.getDevices to enumerate devices by their VID/PID | |
53 pair. | |
54 </p> | |
55 <p> | |
56 You must declare the VID/PID pairs for each type of device you want to use | |
57 under the "usbDevices" permission in your app's manifest file, as shown in the | |
58 example below:</p> | |
59 | |
60 <pre> | |
61 "permissions": [ | |
62 "usbDevices": [ | |
63 { | |
64 "vendorId": 123, | |
65 "productId": 456 | |
66 } | |
67 ] | |
68 ] | |
69 </pre> | |
70 | |
71 <p class="note">Note that only decimal numbers are allowed in JSON format. | |
72 You cannot use hexadecimal numbers in these fields.</p> | |
73 | |
42 <h3 id="finding_device">Finding a device</h3> | 74 <h3 id="finding_device">Finding a device</h3> |
43 | 75 |
44 <p> | 76 <p> |
45 Every device in a USB bus is identified | 77 To determine whether one or more specific devices are connected to a user's |
46 by its vendor and product IDs. | 78 system, use the $ref:usb.getDevices method: |
47 To find a device, | 79 </p> |
48 use the <code>findDevices()</code> method | 80 |
49 which has two parameters: | 81 <pre> |
50 </p> | 82 chrome.usb.getDevices(enumerateDevicesOptions, callback); |
51 | 83 </pre> |
52 <pre> | 84 |
53 chrome.usb.findDevices(FindDeviceOptions, callback) | 85 <br/> |
54 </pre> | 86 |
55 | 87 <table class="simple"> |
56 <br> | 88 <tr> |
57 | 89 <th scope="col">Parameter (type)</th> |
58 <table class="simple"> | 90 <th scope="col">Description</th> |
59 <tr> | 91 </tr> |
60 <th scope="col"> Parameter (type) </th> | 92 <tr> |
61 <th scope="col"> Description </th> | 93 <td>EnumerateDevicesOptions (object)</td> |
62 </tr> | 94 <td>An object specifying both a <code>vendorId</code> (long) and |
63 <tr> | |
64 <td>FindDeviceOptions (object)</td> | |
65 <td>An object specifying both a <code>vendorId</code> (long) and | |
66 <code>productId</code> (long) used to find the correct type of device on | 95 <code>productId</code> (long) used to find the correct type of device on |
67 the bus. Your manifest must declare the<code>usbDevices</code> permission | 96 the bus. Your manifest must declare the<code>usbDevices</code> permission |
Andy
2013/09/06 23:15:01
add a space after the second "the"
Bei Zhang
2013/09/09 22:02:34
Done.
| |
68 section listing all the <code>vendorId</code> and | 97 section listing all the <code>vendorId</code> and <code>deviceId</code> |
69 <code>deviceId</code> pairs your app wants to access. | 98 pairs your app wants to access. |
70 </td> | 99 </td> |
71 </tr> | 100 </tr> |
72 <tr> | 101 <tr> |
73 <td>callback (function)</td> | 102 <td>callback (function)</td> |
74 <td>Called when the device scan is finished. | 103 <td>Called when the device enumeration is finished. The callback will be |
75 The callback will be executed with one parameter, an array of device objec ts | 104 executed with one parameter, an array of <code>Device</code> objects with |
76 with three properties: <code>handle</code>, | 105 three properties: <code>device</code>, <code>vendorId</code>, |
77 <code>vendorId</code>, | 106 <code>productId</code>. The device property is a stable identifier for a |
78 <code>productId</code>. If the optional permissions for this USB device | 107 connected device. It will not change until the device is unplugged. The |
79 were not declared in the extension manifest or the user did not consent | 108 detail of the identifier is opaque and subject to change. Do not rely on |
80 to the permissions requested, the parameter will be <code>null</code>. | 109 its current type. <br/> |
81 If no devices could be found, the array will be empty.</td> | 110 If no devices are found, the array will be empty. |
111 </td> | |
82 </tr> | 112 </tr> |
83 </table> | 113 </table> |
84 | 114 |
85 <p> | 115 <p>Example:</p> |
86 Example: | 116 |
87 </p> | 117 <pre> |
88 | 118 function onDeviceFound(devices) { |
89 <pre> | 119 this.devices=devices; |
90 var onDeviceFound = function(devices) { | |
91 _this.devices=devices; | |
92 if (devices) { | 120 if (devices) { |
93 if (devices.length > 0) { | 121 if (devices.length > 0) { |
94 console.log("Device(s) found: "+devices.length); | 122 console.log("Device(s) found: "+devices.length); |
95 } else { | 123 } else { |
96 console.log("Device could not be found"); | 124 console.log("Device could not be found"); |
97 } | 125 } |
98 } else { | 126 } else { |
99 console.log("Did not request correct permissions"); | 127 console.log("Permission denied."); |
128 } | |
129 } | |
130 | |
131 chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, onDeviceFo und); | |
132 </pre> | |
133 | |
134 <h3 id="usb_open">Opening a device</h3> | |
135 <p> | |
136 Once the <code>Device</code> objects are returned, you can open it using | |
Andy
2013/09/06 23:15:01
"it" -> "a device"
("it" does not have a clear an
Bei Zhang
2013/09/09 22:02:34
Done.
| |
137 usb.openDevice to obtain a connection handle. You can only | |
138 communicate with USB devices using connection handles. | |
139 </p> | |
140 | |
141 <table class="simple"> | |
142 <tr> | |
143 <th scope="col">Property</th> | |
144 <th scope="col">Description</th> | |
145 </tr> | |
146 <tr> | |
147 <td>device</td> | |
148 <td>Object received in $ref:usb.getDevices callback.</td> | |
149 </tr> | |
150 <tr> | |
151 <td>data (arraybuffer)</td> | |
152 <td>Contains the data sent by the device if the transfer was inbound.</td> | |
153 </tr> | |
154 </table> | |
155 | |
156 <p>Example:</p> | |
157 | |
158 <pre> | |
159 var usbConnection = null; | |
160 var onOpenCallback = function(connection) { | |
161 if (connection) { | |
162 usbConnection = connection; | |
163 console.log("Device opened."); | |
164 } else { | |
165 console.log("Device failed to open."); | |
100 } | 166 } |
101 }; | 167 }; |
102 | 168 |
103 chrome.usb.findDevices({"vendorId": vendorId, "productId": productId}, onDeviceF ound); | 169 chrome.usb.openDevice(device, onOpenCallback); |
170 </pre> | |
171 | |
172 <p class="note"> | |
173 Not every device can be opened successfully. In general, OS locked down many | |
Andy
2013/09/06 23:15:01
"OS locked" -> "operating systems lock"
Bei Zhang
2013/09/09 22:02:34
Done.
| |
174 types of USB interfaces (e.g. keyboard and mice, mass storage devices, WebCams, | |
175 and etc.) and they cannot be claimed by user application. On Linux (other than | |
Andy
2013/09/06 23:15:01
1) Delete the first "and"
2) "application" -> "ap
Bei Zhang
2013/09/09 22:02:34
Done.
| |
176 Chrome OS), once an interface of a device is locked down by the OS, the whole | |
177 device is locked down (because all the interfaces shares the same device file), | |
178 even if the other interfaces of the device can be used in theory. On Chrome OS, | |
179 you can request access to unlocked interfaces using the | |
180 $ref:usb.requestAccess method. If permitted, the permission broker will | |
181 unlock the device file for you. | |
182 </p> | |
183 | |
184 <p> | |
185 To simplify the opening process, you can use the $ref:usb.findDevices | |
186 method, which enumerates, requests access, and opens the devices in one call: | |
Andy
2013/09/06 23:15:01
Delete "the". You're not talking about any specif
Bei Zhang
2013/09/09 22:02:34
Done.
| |
187 </p> | |
188 | |
189 <pre> | |
190 chrome.usb.findDevices({"vendorId": vendorId, "productId": productId, "interface Id": interfaceId}, callback); | |
191 </pre> | |
192 <p>which equivalents to:</p> | |
Andy
2013/09/06 23:15:01
Replace this line with: "which is equivalent to"
Bei Zhang
2013/09/09 22:02:34
Done.
| |
193 <pre> | |
194 chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, function ( devices) { | |
195 if (!devices) { | |
196 console.log("Error enumerating devices."); | |
197 callback(); | |
198 return; | |
199 } | |
200 var connections = [], pendingAccessRequests = devices.length; | |
201 devices.forEach(function (device) { | |
202 chrome.usb.requestAccess(interfaceId, function () { | |
203 // No need to check for errors at this point. | |
204 // Nothing can be done if an error occurs anyway. You should always try | |
205 // to open the device. | |
206 chrome.usb.openDevices(device, function (connection) { | |
207 if (connection) connections.push(connection); | |
208 pendingAccessRequests--; | |
209 if (pendingAccessRequests == 0) { | |
210 callback(connections); | |
211 } | |
212 }); | |
213 }); | |
214 }) | |
215 }); | |
104 </pre> | 216 </pre> |
105 | 217 |
106 <h3 id="usb_transfers">USB transfers and receiving data from a device</h3> | 218 <h3 id="usb_transfers">USB transfers and receiving data from a device</h3> |
107 | 219 |
108 <p> | 220 <p> |
109 USB protocol defines four types of transfers: | 221 The USB protocol defines four types of transfers: |
110 <a href="#control_transfers">control</a>, <a href="#bulk_transfers">bulk</a>, <a href="#isochronous_transfers">isochronous</a> and <a href="#interrupt_transfers ">interrupt</a>. | 222 <a href="#control_transfers">control</a>, |
111 Theoretically they can occur in both directions:<br> | 223 <a href="#bulk_transfers">bulk</a>, |
112 device-to-host (inbound) and host-to-device (outbound). | 224 <a href="#isochronous_transfers">isochronous</a> |
113 </p> | 225 and <a href="#interrupt_transfers">interrupt</a>. |
114 | 226 These transfers are described below. |
115 <p> | 227 </p> |
116 However, due to the nature of the USB protocol, both inbound and outbound messag es must be initiated by the host (your computer). For inbound (device-to-host) m essages, the host, your JavaScript code, sends a message flagged as "inbound" to the device. The exact contents of the message depends on the device, but usuall y will have some identification of what you are requesting from it. The device t hen responds with the requested data. The device's response is handled by Chrome and delivered asynchronously to the callback you specified in the transfer meth od. | 228 |
117 An outbound (host-to-device) message is similar, but the response doesn't contai n data returned from the device.</p> | 229 <p> |
118 | 230 Transfers can occur in both directions: device-to-host (inbound), and |
119 <p>For each message from the device, | 231 host-to-device (outbound). Due to the nature of the USB protocol, |
120 the specified callback will receive | 232 both inbound and outbound messages must be initiated by the host (the |
121 an event object with the following properties: | 233 computer runs the Chrome App). |
Andy
2013/09/06 23:15:01
1) "runs" -> "that runs"
2) "App" -> "app" (lower
Bei Zhang
2013/09/09 22:02:34
Done.
| |
234 For inbound (device-to-host) messages, the host (initiated by your JavaScript | |
235 code) sends a message flagged as "inbound" to the device. | |
236 The detail of the message depends on the device, but usually will have | |
Andy
2013/09/06 23:15:01
1) "detail" -> "details" (plural)
2) "depends" ->
Bei Zhang
2013/09/09 22:02:34
Done.
| |
237 some identification of what you are requesting from it. | |
238 The device then responds with the requested data. | |
239 The device's response is handled by Chrome and delivered asynchronously to the | |
240 callback you specify in the transfer method. | |
241 An outbound (host-to-device) message is similar, but the response doesn't | |
242 contain data returned from the device. | |
243 </p> | |
244 | |
245 <p> | |
246 For each message from the device, the specified callback will receive an event | |
247 object with the following properties: | |
122 </p> | 248 </p> |
123 | 249 |
124 <br> | 250 <br> |
125 | 251 |
126 <table class="simple"> | 252 <table class="simple"> |
127 <tr> | 253 <tr> |
128 <th scope="col"> Property </th> | 254 <th scope="col">Property</th> |
129 <th scope="col"> Description </th> | 255 <th scope="col">Description</th> |
130 </tr> | 256 </tr> |
131 <tr> | 257 <tr> |
132 <td>resultCode (integer)</td> | 258 <td>resultCode (integer)</td> |
133 <td>0 is success; other values indicate failure. An error string can be read from | 259 <td>0 is success; other values indicate failure. An error string can be<br/> |
134 <code>chrome.extension.lastError</code> when a failure is indicated.</td> | 260 read from <code>chrome.extension.lastError</code> when a failure is<br/> |
261 indicated. | |
262 </td> | |
135 </tr> | 263 </tr> |
136 <tr> | 264 <tr> |
137 <td>data (arraybuffer)</td> | 265 <td>data (arraybuffer)</td> |
138 <td>Contains the data sent by the device if transfer was inbound. | 266 <td>Contains the data sent by the device if the transfer was inbound.</td> |
139 </td> | |
140 </tr> | 267 </tr> |
141 </table> | 268 </table> |
142 | 269 |
143 <p> | 270 <p>Example:</p> |
144 Example: | |
145 </p> | |
146 | 271 |
147 <pre> | 272 <pre> |
148 var onTransferCallback = function(event) { | 273 var onTransferCallback = function(event) { |
149 if (event && event.resultCode === 0 && event.data) { | 274 if (event && event.resultCode === 0 && event.data) { |
150 console.log("got "+event.data.byteLength+" bytes"); | 275 console.log("got " + event.data.byteLength + " bytes"); |
151 } | 276 } |
152 }; | 277 }; |
153 | 278 |
154 chrome.usb.bulkTransfer(device, transferInfo, onTransferCallback); | 279 chrome.usb.bulkTransfer(connectionHandle, transferInfo, onTransferCallback); |
155 </pre> | 280 </pre> |
156 | 281 |
157 <h3 id="control_transfers">CONTROL transfers</h3> | 282 <h3 id="control_transfers">CONTROL transfers</h3> |
158 | 283 |
159 <p> | 284 <p>Control transfers are generally used to send or receive configuration or |
160 Control transfers are generally used to send or receive configuration | 285 command parameters to a USB device. The controlTransfer method always sends to |
161 or command parameters to a USB device. | 286 /reads from endpoint 0, and no claimInterface is required. |
Andy
2013/09/06 23:15:01
Delete spaces before "/".
Bei Zhang
2013/09/09 22:02:34
Done.
| |
162 The method is simple and receives three parameters: | 287 The method is simple and receives three parameters:</p> |
163 </p> | 288 |
164 | 289 <pre> |
165 <pre> | 290 chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback) |
166 chrome.usb.controlTransfer(deviceObj, transferInfo, transferCallback) | |
167 </pre> | 291 </pre> |
168 | 292 |
169 <br> | 293 <br> |
170 | 294 |
171 <table class="simple"> | 295 <table class="simple"> |
172 <tr> | 296 <tr> |
173 <th scope="col"> Parameter (types)</th> | 297 <th scope="col">Parameter (types)</th> |
174 <th scope="col"> Description </th> | 298 <th scope="col">Description</th> |
175 </tr> | 299 </tr> |
176 <tr> | 300 <tr> |
177 <td>deviceObj</td> | 301 <td>connectionHandle</td> |
178 <td>Object sent in <code>findDevice()</code> callback.</td> | 302 <td>Object received in $ref:usb.openDevice callback. |
303 </td> | |
179 </tr> | 304 </tr> |
180 <tr> | 305 <tr> |
181 <td>transferInfo</td> | 306 <td>transferInfo</td> |
182 <td>Parameter object with values from the table below. | 307 <td>Parameter object with values from the table below. Check your USB |
183 Check your USB device protocol specification for specifics.</td> | 308 device protocol specification for details. |
309 </td> | |
184 </tr> | 310 </tr> |
185 <tr> | 311 <tr> |
186 <td>transferCallback()</td> | 312 <td>transferCallback()</td> |
187 <td>Invoked when the transfer has completed. | 313 <td>Invoked when the transfer has completed.</td> |
188 </td> | |
189 </tr> | 314 </tr> |
190 </table> | 315 </table> |
191 | 316 |
192 <p> | 317 <p> |
193 Values for <code>transferInfo</code> object: | 318 Values for |
194 </p> | 319 <code>transferInfo</code> |
195 | 320 object: |
196 <table class="simple"> | 321 </p> |
197 <tr> | 322 |
198 <th scope="col"> Value </th> | 323 <table class="simple"> |
199 <th scope="col"> Description </th> | 324 <tr> |
200 </tr> | 325 <th scope="col">Value</th> |
201 <tr> | 326 <th scope="col">Description</th> |
202 <td>requestType (string)</td> | 327 </tr> |
328 <tr> | |
329 <td>requestType (string)</td> | |
203 <td>"vendor", "standard", "class" or "reserved".</td> | 330 <td>"vendor", "standard", "class" or "reserved".</td> |
204 </tr> | 331 </tr> |
205 <tr> | 332 <tr> |
206 <td>recipient (string)</td> | 333 <td>recipient (string)</td> |
207 <td>"device", "interface", "endpoint" or "other".</td> | 334 <td>"device", "interface", "endpoint" or "other".</td> |
208 </tr> | 335 </tr> |
209 <tr> | 336 <tr> |
210 <td>direction (string)</td> | 337 <td>direction (string)</td> |
211 <td>"in" or "out". | 338 <td>"in" or "out". The "in" direction is used to notify the device that<br/> |
212 "in" direction is used to notify the device | 339 it should send information to the host. All communication on a USB<br/> |
213 that it should send information to the host. | 340 bus is host-initiated, so use an 'in' transfer to allow a device to<br/> |
Andy
2013/09/06 23:15:01
Use double-quotes around "in" for consistency.
Bei Zhang
2013/09/09 22:02:34
Done.
| |
214 All communication in a USB bus is host-initiated, | 341 send information back. |
215 so use an 'in' transfer to allow a device | 342 </td> |
216 to send information back.</td> | 343 </tr> |
217 </tr> | 344 <tr> |
218 <tr> | 345 <td>request (integer)</td> |
219 <td>request (integer)</td> | |
220 <td>Defined by your device's protocol.</td> | 346 <td>Defined by your device's protocol.</td> |
221 </tr> | 347 </tr> |
222 <tr> | 348 <tr> |
223 <td>value (integer)</td> | 349 <td>value (integer)</td> |
224 <td>Defined by your device's protocol.</td> | 350 <td>Defined by your device's protocol.</td> |
225 </tr> | 351 </tr> |
226 <tr> | 352 <tr> |
227 <td>index (integer)</td> | 353 <td>index (integer)</td> |
228 <td>Defined by your device's protocol.</td> | 354 <td>Defined by your device's protocol.</td> |
229 </tr> | 355 </tr> |
230 <tr> | 356 <tr> |
231 <td>length (integer)</td> | 357 <td>length (integer)</td> |
232 <td>Only used when direction is "in". | 358 <td>Only used when direction is "in". Notifies the device that this is |
233 Notifies the device that this is the amount | 359 the amount of data the host is expecting in response. |
234 of data the host is expecting in response.</td> | 360 </td> |
235 </tr> | 361 </tr> |
236 <tr> | 362 <tr> |
237 <td>data (arraybuffer)</td> | 363 <td>data (arraybuffer)</td> |
238 <td>Defined by your device's protocol, | 364 <td>Defined by your device's protocol, required when direction is |
239 required when direction is "out".</td> | 365 "out". |
366 </td> | |
240 </tr> | 367 </tr> |
241 </table> | 368 </table> |
242 | 369 |
243 <p> | 370 <p>Example:</p> |
244 Example: | |
245 </p> | |
246 | 371 |
247 <pre> | 372 <pre> |
248 var transferInfo = { | 373 var transferInfo = { |
249 "requestType": "vendor", | 374 "requestType": "vendor", |
250 "recipient": "device", | 375 "recipient": "device", |
251 "direction": "out", | 376 "direction": "out", |
252 "request": 0x31, | 377 "request": 0x31, |
253 "value": 120, | 378 "value": 120, |
254 "index": 0, | 379 "index": 0, |
255 "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer | 380 // Note that the ArrayBuffer, not the TypedArray itself is used. |
256 }; | 381 "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer |
257 chrome.usb.controlTransfer(deviceObj, transferInfo, optionalCallback); | 382 }; |
383 chrome.usb.controlTransfer(connectionHandle, transferInfo, optionalCallback); | |
258 </pre> | 384 </pre> |
259 | 385 |
260 <h3 id="isochronous_transfers">ISOCHRONOUS transfers</h3> | 386 <h3 id="isochronous_transfers">ISOCHRONOUS transfers</h3> |
261 | 387 |
262 <p> | 388 <p>Isochronous transfer is the most complex type of USB transfers. They are |
Andy
2013/09/06 23:15:01
"USB transfers" -> "USB transfer" (singular)
Bei Zhang
2013/09/09 22:02:34
Done.
| |
263 Isochronous transfers is the most complex type of USB transfers. They are common ly used for streams of data, like video and sound. To initiate an isochronous tr ansfer (either inbound or outbound), you must use: | 389 commonly used for streams of data, like video and sound. To initiate an |
264 </p> | 390 isochronous transfer (either inbound or outbound), you must use |
265 | 391 the $ref:usb.isochronousTransfer method:</p> |
266 <pre> | 392 |
267 chrome.usb.isochronousTransfer(deviceObj, isochronousTransferInfo, transferCallb ack) | 393 <pre> |
268 </pre> | 394 chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transf erCallback) |
269 | 395 </pre> |
270 <br> | 396 |
271 | 397 <br/> |
272 <table class="simple"> | 398 |
273 <tr> | 399 <table class="simple"> |
274 <th scope="col"> Parameter </th> | 400 <tr> |
275 <th scope="col"> Description </th> | 401 <th scope="col">Parameter</th> |
276 </tr> | 402 <th scope="col">Description</th> |
277 <tr> | 403 </tr> |
278 <td>deviceObj</td> | 404 <tr> |
279 <td>Object sent on <code>findDevice()</code> callback.</td> | 405 <td>connectionHandle</td> |
406 <td>Object received in $ref:usb.openDevice callback. | |
407 </td> | |
280 </tr> | 408 </tr> |
281 <tr> | 409 <tr> |
282 <td>isochronousTransferInfo</td> | 410 <td>isochronousTransferInfo</td> |
283 <td>Parameter object with the values in the table below.</td> | 411 <td>Parameter object with the values in the table below.</td> |
284 </tr> | 412 </tr> |
285 <tr> | 413 <tr> |
286 <td>transferCallback()</td> | 414 <td>transferCallback()</td> |
287 <td>Invoked when the transfer has completed. | 415 <td>Invoked when the transfer has completed.</td> |
288 </td> | |
289 </tr> | 416 </tr> |
290 </table> | 417 </table> |
291 | 418 |
292 <p> | 419 <p> |
293 Values for <code>isochronousTransferInfo</code> object: | 420 Values for |
421 <code>isochronousTransferInfo</code> | |
422 object: | |
294 </p> | 423 </p> |
295 | 424 |
296 <table class="simple"> | 425 <table class="simple"> |
297 <tr> | 426 <tr> |
298 <th scope="col"> Value </th> | 427 <th scope="col">Value</th> |
299 <th scope="col"> Description </th> | 428 <th scope="col">Description</th> |
300 </tr> | 429 </tr> |
301 <tr> | 430 <tr> |
302 <td>transferInfo (object)</td> | 431 <td>transferInfo (object)</td> |
303 <td>An object with the following attributes:<br> | 432 <td>An object with the following attributes:<br/> |
304 <b>direction (string): </b>"in" or "out".<br> | 433 <b>direction (string): </b>"in" or "out".<br/> |
305 <b>endpoint (integer): </b>defined by your device. Usually can be found by looking at an USB instrospection tool, like <code>lsusb -v</code><br> | 434 <b>endpoint (integer): </b>defined by your device. Usually can be found by |
306 <b>length (integer): </b>only used when direction is "in". | 435 looking at an USB instrospection tool, like <code>lsusb -v</code><br/> |
307 Notifies the device that this is the amount | 436 <b>length (integer): </b>only |
308 of data the host is expecting in response. Should be AT LEAST <code>packet s * packetLength</code><br> | 437 used when direction is "in". Notifies the device that this is the amount |
438 of data the host is expecting in response. <br/> | |
439 | |
440 Should be AT LEAST <code>packets</code> × <code>packetLength</code>. | |
441 <br/> | |
309 <b>data (arraybuffer): </b>defined by your device's protocol; | 442 <b>data (arraybuffer): </b>defined by your device's protocol; |
310 only used when direction is "out". | 443 only used when direction is "out". |
311 </td> | 444 </td> |
312 </tr> | 445 </tr> |
313 <tr> | 446 <tr> |
314 <td>packets (integer)</td> | 447 <td>packets (integer)</td> |
315 <td>Total number of packets expected in this transfer.</td> | 448 <td>Total number of packets expected in this transfer.</td> |
316 </tr> | 449 </tr> |
317 <tr> | 450 <tr> |
318 <td>packetLength (integer)</td> | 451 <td>packetLength (integer)</td> |
319 <td>Expected length of each packet in this transfer.</td> | 452 <td>Expected length of each packet in this transfer.</td> |
320 </tr> | 453 </tr> |
321 </table> | 454 </table> |
322 | 455 |
323 <p> | 456 <p>Example:</p> |
324 Example: | |
325 </p> | |
326 | 457 |
327 <pre> | 458 <pre> |
328 var transferInfo = { | 459 var transferInfo = { |
329 "direction": "in", | 460 "direction": "in", |
330 "endpoint": 1, | 461 "endpoint": 1, |
331 "length": 2560 | 462 "length": 2560 |
332 }; | 463 }; |
464 | |
333 var isoTransferInfo = { | 465 var isoTransferInfo = { |
334 "transferInfo": transferInfo, | 466 "transferInfo": transferInfo, |
335 "packets": 20, | 467 "packets": 20, |
336 "packetLength": 128 | 468 "packetLength": 128 |
337 }; | 469 }; |
338 chrome.usb.isochronousTransfer(deviceObj, isoTransferInfo, optionalCallback); | 470 |
471 chrome.usb.isochronousTransfer(connectionHandle, isoTransferInfo, optionalCallba ck); | |
339 </pre> | 472 </pre> |
340 | 473 |
341 <p> | 474 <p class="note"> |
342 <b>Notes:</b> One isochronous transfer will contain <code>isoTransferInfo.pack ets</code> packets of <code>isoTransferInfo.packetLength</code> bytes. If it is an inbound transfer (your code requested data from the device), the <code>data</ code> field in the onUsbEvent will be an ArrayBuffer of size <code>transferInfo. length</code>. It is your duty to walk through this ArrayBuffer and extract the different packets, each starting at a multiple of <code>isoTransferInfo.packetLe ngth</code> bytes. If you are expecting a stream of data from the device, rememb er that you will have to send one "inbound" transfer for each transfer you expec t back. USB devices don't send transfers to the bus unless the host explicitly r equests them through "inbound" transfers. | 475 <b>Notes:</b> One isochronous transfer will contain |
476 <code>isoTransferInfo.packets</code> packets of | |
477 <code>isoTransferInfo.packetLength</code> bytes. | |
478 If it is an inbound transfer (your code requested data from the device), the | |
479 <code>data</code> field in the onUsbEvent will be an ArrayBuffer of size | |
480 <code>transferInfo.length</code>. It is your duty to walk through this | |
481 ArrayBuffer and extract the different packets, each starting at a multiple of | |
482 <code>isoTransferInfo.packetLength</code> bytes. | |
483 </p> | |
484 | |
485 <p class="note"> | |
Andy
2013/09/06 23:15:01
It's awkward to have two "note" paragraphs in a ro
Bei Zhang
2013/09/09 22:02:34
Done.
| |
486 If you are expecting a stream of data from the device, remember that | |
487 you will have to send one "inbound" transfer for each transfer you expect | |
488 back. USB devices don't send transfers to the USB bus unless the host | |
489 explicitly requests them through "inbound" transfers. | |
343 </p> | 490 </p> |
344 | 491 |
345 <h3 id="bulk_transfers">BULK transfers</h3> | 492 <h3 id="bulk_transfers">BULK transfers</h3> |
346 | 493 |
347 <p> | 494 <p>Bulk transfers are commonly used to transfer a large amount of non-time |
Andy
2013/09/06 23:15:01
Use dashes to link all 3 words: "non", "time", and
Bei Zhang
2013/09/09 22:02:34
Done.
| |
348 Bulk transfer is an USB transfer type commonly used | 495 sensitive data in a reliable way. |
349 to transfer a large amount of data in a reliable way. | 496 $ref:usb.bulkTransfer has three parameters:</p> |
350 The method has three parameters: | |
351 </p> | |
352 | 497 |
353 <pre> | 498 <pre> |
354 chrome.usb.bulkTransfer(deviceObj, transferInfo, transferCallback) | 499 chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback); |
355 </pre> | 500 </pre> |
356 | 501 |
357 <br> | 502 <br> |
358 | 503 |
359 <table class="simple"> | 504 <table class="simple"> |
360 <tr> | 505 <tr> |
361 <th scope="col"> Parameter </th> | 506 <th scope="col">Parameter</th> |
362 <th scope="col"> Description </th> | 507 <th scope="col">Description</th> |
363 </tr> | 508 </tr> |
364 <tr> | 509 <tr> |
365 <td>deviceObj</td> | 510 <td>connectionHandle</td> |
366 <td>Object sent on <code>findDevice()</code> callback.</td> | 511 <td>Object received in $ref:usb.openDevice callback. |
512 </td> | |
367 </tr> | 513 </tr> |
368 <tr> | 514 <tr> |
369 <td>transferInfo</td> | 515 <td>transferInfo</td> |
370 <td>Parameter object with the values in the table below.</td> | 516 <td>Parameter object with the values in the table below.</td> |
371 </tr> | 517 </tr> |
372 <tr> | 518 <tr> |
373 <td>transferCallback</td> | 519 <td>transferCallback</td> |
374 <td>Invoked when the transfer has completed. | 520 <td>Invoked when the transfer has completed.</td> |
375 </td> | |
376 </tr> | 521 </tr> |
377 </table> | 522 </table> |
378 | 523 |
379 <p> | 524 <p> |
380 Values for <code>transferInfo</code> object: | 525 Values for |
526 <code>transferInfo</code> | |
527 object: | |
381 </p> | 528 </p> |
382 | 529 |
383 <table class="simple"> | 530 <table class="simple"> |
384 <tr> | 531 <tr> |
385 <th scope="col"> Value </th> | 532 <th scope="col">Value</th> |
386 <th scope="col"> Description </th> | 533 <th scope="col">Description</th> |
387 </tr> | 534 </tr> |
388 <tr> | 535 <tr> |
389 <td>direction (string)</td> | 536 <td>direction (string)</td> |
390 <td>"in" or "out".</td> | 537 <td>"in" or "out".</td> |
391 </tr> | 538 </tr> |
392 <tr> | 539 <tr> |
393 <td>endpoint (integer)</td> | 540 <td>endpoint (integer)</td> |
394 <td>Defined by your device's protocol.</td> | 541 <td>Defined by your device's protocol.</td> |
395 </tr> | 542 </tr> |
396 <tr> | 543 <tr> |
397 <td>length (integer)</td> | 544 <td>length (integer)</td> |
398 <td>Only used when direction is "in". | 545 <td>Only used when direction is "in". Notifies the device that this is |
399 Notifies the device that this is the amount | 546 the amount of data the host is expecting in response. |
400 of data the host is expecting in response.</td> | 547 </td> |
401 </tr> | 548 </tr> |
402 <tr> | 549 <tr> |
403 <td>data (ArrayBuffer)</td> | 550 <td>data (ArrayBuffer)</td> |
404 <td>Defined by your device's protocol; | 551 <td>Defined by your device's protocol; only used when direction is |
405 only used when direction is "out".</td> | 552 "out". |
553 </td> | |
406 </tr> | 554 </tr> |
407 </table> | 555 </table> |
408 | 556 |
409 <p> | 557 <p>Example:</p> |
410 Example: | |
411 </p> | |
412 | 558 |
413 <pre> | 559 <pre> |
414 var transferInfo = { | 560 var transferInfo = { |
415 "direction": "out", | 561 "direction": "out", |
416 "endpoint": 1, | 562 "endpoint": 1, |
417 "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer | 563 "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer |
418 }; | 564 }; |
419 </pre> | 565 </pre> |
420 | 566 |
421 <h3 id="interrupt_transfers">INTERRUPT transfers</h3> | 567 <h3 id="interrupt_transfers">INTERRUPT transfers</h3> |
422 | 568 |
423 <p> | 569 <p>Interrupt transfers are used to time sensitive, small amount of data. |
Andy
2013/09/06 23:15:01
Try this: "... used to transfer small amounts of t
Bei Zhang
2013/09/09 22:02:34
Done.
| |
424 Interrupt transfers are used to send important notifications. | 570 Since all USB communication is initiated by the host, host code usually polls |
425 Since all USB communication is initiated by the host, | 571 the device periodically, sending interrupt IN transfers that will make the |
426 host code usually polls the device periodically, | 572 device send data back if there is anything in the interrupt queue (maintained |
427 sending interrupt IN transfers that will make the device send data back | 573 by the device). |
428 if there is anything in the interrupt queue. | 574 $ref:usb.interruptTransfer has three parameters:</p> |
429 The method has three parameters: | |
430 </p> | |
431 | 575 |
432 <pre> | 576 <pre> |
433 chrome.usb.interruptTransfer(deviceObj, transferInfo, transferCallback) | 577 chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback); |
434 </pre> | 578 </pre> |
435 | 579 |
436 <br> | 580 <br> |
437 | 581 |
438 <table class="simple"> | 582 <table class="simple"> |
439 <tr> | 583 <tr> |
440 <th scope="col"> Parameter </th> | 584 <th scope="col">Parameter</th> |
441 <th scope="col"> Description </th> | 585 <th scope="col">Description</th> |
442 </tr> | 586 </tr> |
443 <tr> | 587 <tr> |
444 <td>deviceObj</td> | 588 <td>connectionHandle</td> |
445 <td>Object sent on <code>findDevice()</code> callback.</td> | 589 <td>Object received in $ref:usb.openDevice callback. |
590 </td> | |
446 </tr> | 591 </tr> |
447 <tr> | 592 <tr> |
448 <td>transferInfo</td> | 593 <td>transferInfo</td> |
449 <td>Parameter object with the values in the table below.</td> | 594 <td>Parameter object with the values in the table below.</td> |
450 </tr> | 595 </tr> |
451 <tr> | 596 <tr> |
452 <td>transferCallback</td> | 597 <td>transferCallback</td> |
453 <td>Invoked when the transfer has completed. | 598 <td>Invoked when the transfer has completed. Notice that this callback |
454 Notice that this callback doesn't contain the device's response. | 599 doesn't contain the device's response. The purpose of the callback is |
455 It's just to notify your code that the asynchronous transfer request | 600 simply to notify your code that the asynchronous transfer requests has |
456 has been processed and you can go ahead. | 601 been processed. |
457 The device's response, if any, will always be sent through | |
458 the <code>onEvent()</code> callback set on <code>findDevice()</code>. | |
459 </td> | 602 </td> |
460 </tr> | 603 </tr> |
461 </table> | 604 </table> |
462 | 605 |
463 <p> | 606 <p> |
464 Values for <code>transferInfo</code> object: | 607 Values for <code>transferInfo</code> object: |
465 </p> | 608 </p> |
466 | 609 |
467 <table class="simple"> | 610 <table class="simple"> |
468 <tr> | 611 <tr> |
469 <th scope="col"> Value </th> | 612 <th scope="col">Value</th> |
470 <th scope="col"> Description </th> | 613 <th scope="col">Description</th> |
471 </tr> | 614 </tr> |
472 <tr> | 615 <tr> |
473 <td>direction (string)</td> | 616 <td>direction (string)</td> |
474 <td>"in" or "out".</td> | 617 <td>"in" or "out".</td> |
475 </tr> | 618 </tr> |
476 <tr> | 619 <tr> |
477 <td>endpoint (integer)</td> | 620 <td>endpoint (integer)</td> |
478 <td>Defined by your device's protocol.</td> | 621 <td>Defined by your device's protocol.</td> |
479 </tr> | 622 </tr> |
480 <tr> | 623 <tr> |
481 <td>length (integer)</td> | 624 <td>length (integer)</td> |
482 <td>Only used when direction is "in". | 625 <td>Only used when direction is "in". Notifies the device that this is |
483 Notifies the device that this is the amount | 626 the amount of data the host is expecting in response. |
484 of data the host is expecting in response.</td> | 627 </td> |
485 </tr> | 628 </tr> |
486 <tr> | 629 <tr> |
487 <td>data (ArrayBuffer)</td> | 630 <td>data (ArrayBuffer)</td> |
488 <td>Defined by your device's protocol; | 631 <td>Defined by your device's protocol; only used when direction is |
489 only used when direction is "out".</td> | 632 "out". |
490 </tr> | 633 </td> |
634 </tr> | |
635 </table> | |
491 | 636 |
492 <p> | 637 <p>Example:</p> |
493 Example: | |
494 </p> | |
495 | 638 |
496 <pre> | 639 <pre> |
497 var transferInfo = { | 640 var transferInfo = { |
498 "direction": "in", | 641 "direction": "in", |
499 "endpoint": 1, | 642 "endpoint": 1, |
500 "length": 2 | 643 "length": 2 |
501 }; | 644 }; |
502 chrome.usb.interruptTransfer(deviceObj, transferInfo, optionalCallback); | 645 chrome.usb.interruptTransfer(connectionHandle, transferInfo, optionalCallback); |
503 </pre> | 646 </pre> |
504 | 647 |
505 <h3 id="caveats">Caveats</h3> | 648 <h3 id="caveats">Caveats</h3> |
506 | 649 |
507 <p> | 650 <p> |
508 On most Linux systems, USB devices are mapped with read-only permissions by defa ult. To access it through this API, your user will need to have write access too . A simple solution is to set a udev rule. Create a file <code>/etc/udev/rules.d /50-yourdevicename.rules</code> | 651 On most Linux systems, USB devices are mapped with read-only permissions by |
509 with the following content: | 652 default. To open the device through this API, your user will need to have |
Andy
2013/09/06 23:15:01
"the" -> "a" (you're not talking about a specific
Bei Zhang
2013/09/09 22:02:34
Done.
| |
653 write access to them too. | |
Andy
2013/09/06 23:15:01
"them too" -> "it"
Bei Zhang
2013/09/09 22:02:34
Done.
| |
654 A simple solution is to set a udev rule. Create a file | |
655 <code>/etc/udev/rules.d/50-yourdevicename.rules</code> | |
656 with the following content: | |
510 </p> | 657 </p> |
511 | 658 |
512 <pre> | 659 <pre> |
513 SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plug dev" | 660 SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plug dev" |
514 </pre> | 661 </pre> |
515 | 662 |
516 <p> | 663 <p> |
517 Then, just restart the udev daemon: <code>service udev restart</code>. You can check if the permissions are correctly set by: | 664 Then, just restart the udev daemon: <code>service udev restart</code>. |
518 <ul> | 665 You can check if device permissions are set correctly by following these |
519 <li>Find the bus and device numbers in <code>lsusb</code></li> | 666 steps: |
520 <li><code>ls -al /dev/bus/usb/[bus]/[device]</code>. This file should be own ed by group "plugdev" and have group write permissions.</li> | |
521 </ul> | |
522 </p> | 667 </p> |
523 | 668 |
524 <p> | 669 <ul> |
525 Not all devices can be accessed through this API. In general, devices are not accessible either because the Operating System's kernel or a native driver holds them off from user space code. Some examples are devices with HID profiles on O SX systems and USB pen drives. | 670 <li>Run <code>lsusb</code> to find the bus and device numbers.</li> |
526 </p> | 671 <li>Run <code>ls -al /dev/bus/usb/[bus]/[device]</code>. This file should be |
672 owned by group "plugdev" and have group write permissions. | |
673 </li> | |
674 </ul> | |
675 | |
676 <p>Your app cannot do this automatically since this needs root access. We | |
Andy
2013/09/06 23:15:01
"this needs" -> "this procedure requires"
Bei Zhang
2013/09/09 22:02:34
Done.
| |
677 recommend the app developers to provide instructions to the end-users and link | |
678 to <a href="#caveats">this page</a> for reasons.</p> | |
Andy
2013/09/06 23:15:01
This sentence is not grammatically correct, and th
Bei Zhang
2013/09/09 22:02:34
Done.
| |
679 | |
680 <p>On Chrome OS, simply call $ref:usb.requestAccess. The permission | |
681 broker does this for you.</p> | |
682 | |
683 <p>Not all devices can be accessed through this API. In general, devices | |
Andy
2013/09/06 23:15:01
1) I would move this paragraph to the top of this
Bei Zhang
2013/09/09 22:02:34
Done.
Bei Zhang
2013/09/09 22:02:34
Done.
| |
684 are not accessible either because the Operating System's kernel or a native | |
685 driver holds them off from user space code. Some examples are devices with | |
686 HID profiles on OSX systems and USB pen drives.</p> | |
Andy
2013/09/06 23:15:01
Add a comma after "systems".
Bei Zhang
2013/09/09 22:02:34
Done.
| |
527 | 687 |
528 <h2 id="serial">Accessing serial devices</h2> | 688 <h2 id="serial">Accessing serial devices</h2> |
529 | 689 |
530 <p> | 690 <p> |
531 You can use the serial API to read | 691 You can use the serial API to read |
532 and write from a serial device. | 692 and write from a serial device. |
533 </p> | 693 </p> |
534 | 694 |
535 <h3 id="requirement">Manifest requirement</h3> | 695 <h3 id="requirement">Manifest requirement</h3> |
536 | 696 |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
597 _this.connectionId = connectionInfo.connectionId; | 757 _this.connectionId = connectionInfo.connectionId; |
598 // Do whatever you need to do with the opened port. | 758 // Do whatever you need to do with the opened port. |
599 } | 759 } |
600 // Open the serial port /dev/ttyS01 | 760 // Open the serial port /dev/ttyS01 |
601 chrome.serial.open("/dev/ttyS01", {bitrate: 115200}, onOpen); | 761 chrome.serial.open("/dev/ttyS01", {bitrate: 115200}, onOpen); |
602 </pre> | 762 </pre> |
603 | 763 |
604 <h3 id="closing">Closing a serial port</h3> | 764 <h3 id="closing">Closing a serial port</h3> |
605 | 765 |
606 <p> | 766 <p> |
607 Closing a serial port is simple but very important. See the example below: | 767 Closing a serial port is simple but very important. See the example below: |
608 </p> | 768 </p> |
609 | 769 |
610 <pre> | 770 <pre> |
611 var onClose = function(result) { | 771 var onClose = function(result) { |
612 console.log("Serial port closed"); | 772 console.log("Serial port closed"); |
613 } | 773 } |
614 chrome.serial.close(connectionId, onClose); | 774 chrome.serial.close(connectionId, onClose); |
615 </pre> | 775 </pre> |
616 | 776 |
617 <h3 id="reading">Reading from a serial port</h3> | 777 <h3 id="reading">Reading from a serial port</h3> |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
680 | 840 |
681 <p> | 841 <p> |
682 You can flush your serial port buffer by issuing the flush command: | 842 You can flush your serial port buffer by issuing the flush command: |
683 </p> | 843 </p> |
684 | 844 |
685 <pre> | 845 <pre> |
686 chrome.serial.flush(connectionId, onFlush); | 846 chrome.serial.flush(connectionId, onFlush); |
687 </pre> | 847 </pre> |
688 | 848 |
689 <p class="backtotop"><a href="#top">Back to top</a></p> | 849 <p class="backtotop"><a href="#top">Back to top</a></p> |
OLD | NEW |