1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 define('serial_service', [
6 'content/public/renderer/service_provider',
7 'device/serial/serial.mojom',
8 'mojo/public/js/bindings/core',
9 'mojo/public/js/bindings/router',
10 ], function(serviceProvider
, serialMojom
, core
, routerModule
) {
12 * A Javascript client for the serial service and connection Mojo services.
14 * This provides a thick client around the Mojo services, exposing a JS-style
15 * interface to serial connections and information about serial devices. This
16 * converts parameters and result between the Apps serial API types and the
20 var service
= new serialMojom
.SerialServiceProxy(new routerModule
.Router(
21 serviceProvider
.connectToService(serialMojom
.SerialServiceProxy
.NAME_
)));
23 function getDevices() {
24 return service
.getDevices().then(function(response
) {
25 return $Array
.map(response
.devices
, function(device
) {
26 var result
= {path
: device
.path
|| ''};
27 if (device
.has_vendor_id
)
28 result
.vendorId
= device
.vendor_id
;
29 if (device
.has_product_id
)
30 result
.productId
= device
.product_id
;
31 if (device
.display_name
)
32 result
.displayName
= device
.display_name
;
38 var DEFAULT_CLIENT_OPTIONS
= {
46 var DATA_BITS_TO_MOJO
= {
47 undefined: serialMojom
.DataBits
.NONE
,
48 'seven': serialMojom
.DataBits
.SEVEN
,
49 'eight': serialMojom
.DataBits
.EIGHT
,
51 var STOP_BITS_TO_MOJO
= {
52 undefined: serialMojom
.StopBits
.NONE
,
53 'one': serialMojom
.StopBits
.ONE
,
54 'two': serialMojom
.StopBits
.TWO
,
56 var PARITY_BIT_TO_MOJO
= {
57 undefined: serialMojom
.ParityBit
.NONE
,
58 'no': serialMojom
.ParityBit
.NO
,
59 'odd': serialMojom
.ParityBit
.ODD
,
60 'even': serialMojom
.ParityBit
.EVEN
,
63 function invertMap(input
) {
65 for (var key
in input
) {
66 if (key
== 'undefined')
67 output
[input
[key
]] = undefined;
69 output
[input
[key
]] = key
;
73 var DATA_BITS_FROM_MOJO
= invertMap(DATA_BITS_TO_MOJO
);
74 var STOP_BITS_FROM_MOJO
= invertMap(STOP_BITS_TO_MOJO
);
75 var PARITY_BIT_FROM_MOJO
= invertMap(PARITY_BIT_TO_MOJO
);
77 function getServiceOptions(options
) {
80 out
.data_bits
= DATA_BITS_TO_MOJO
[options
.dataBits
];
82 out
.stop_bits
= STOP_BITS_TO_MOJO
[options
.stopBits
];
83 if (options
.parityBit
)
84 out
.parity_bit
= PARITY_BIT_TO_MOJO
[options
.parityBit
];
85 if ('ctsFlowControl' in options
) {
86 out
.has_cts_flow_control
= true;
87 out
.cts_flow_control
= options
.ctsFlowControl
;
89 if ('bitrate' in options
)
90 out
.bitrate
= options
.bitrate
;
94 function convertServiceInfo(result
) {
96 throw new Error('Failed to get ConnectionInfo.');
98 ctsFlowControl
: !!result
.info
.cts_flow_control
,
99 bitrate
: result
.info
.bitrate
|| undefined,
100 dataBits
: DATA_BITS_FROM_MOJO
[result
.info
.data_bits
],
101 stopBits
: STOP_BITS_FROM_MOJO
[result
.info
.stop_bits
],
102 parityBit
: PARITY_BIT_FROM_MOJO
[result
.info
.parity_bit
],
106 function Connection(remoteConnection
, router
, id
, options
) {
107 this.remoteConnection_
= remoteConnection
;
108 this.router_
= router
;
110 getConnections().then(function(connections
) {
111 connections
[this.id_
] = this;
113 this.paused_
= false;
115 for (var key
in DEFAULT_CLIENT_OPTIONS
) {
116 this.options_
[key
] = DEFAULT_CLIENT_OPTIONS
[key
];
118 this.setClientOptions_(options
);
121 Connection
.create = function(path
, options
) {
122 options
= options
|| {};
123 var serviceOptions
= getServiceOptions(options
);
124 var pipe
= core
.createMessagePipe();
125 // Note: These two are created and closed because the service implementation
126 // requires that we provide valid message pipes for the data source and
127 // sink. Currently the client handles are immediately closed; the real
128 // implementation will come later.
129 var sendPipe
= core
.createMessagePipe();
130 var receivePipe
= core
.createMessagePipe();
131 service
.connect(path
,
135 receivePipe
.handle0
);
136 core
.close(sendPipe
.handle1
);
137 core
.close(receivePipe
.handle1
);
138 var router
= new routerModule
.Router(pipe
.handle1
);
139 var connection
= new serialMojom
.ConnectionProxy(router
);
140 return connection
.getInfo().then(convertServiceInfo
).then(
142 return Promise
.all([info
, allocateConnectionId()]);
143 }).catch(function(e
) {
146 }).then(function(results
) {
147 var info
= results
[0];
149 var serialConnectionClient
= new Connection(
150 connection
, router
, id
, options
);
151 var clientInfo
= serialConnectionClient
.getClientInfo_();
152 for (var key
in clientInfo
) {
153 info
[key
] = clientInfo
[key
];
156 connection
: serialConnectionClient
,
162 Connection
.prototype.close = function() {
163 this.router_
.close();
164 return getConnections().then(function(connections
) {
165 delete connections
[this.id_
]
170 Connection
.prototype.getClientInfo_ = function() {
172 connectionId
: this.id_
,
173 paused
: this.paused_
,
175 for (var key
in this.options_
) {
176 info
[key
] = this.options_
[key
];
181 Connection
.prototype.getInfo = function() {
182 var info
= this.getClientInfo_();
183 return this.remoteConnection_
.getInfo().then(convertServiceInfo
).then(
185 for (var key
in result
) {
186 info
[key
] = result
[key
];
189 }).catch(function() {
194 Connection
.prototype.setClientOptions_ = function(options
) {
195 if ('name' in options
)
196 this.options_
.name
= options
.name
;
197 if ('receiveTimeout' in options
)
198 this.options_
.receiveTimeout
= options
.receiveTimeout
;
199 if ('sendTimeout' in options
)
200 this.options_
.sendTimeout
= options
.sendTimeout
;
201 if ('bufferSize' in options
)
202 this.options_
.bufferSize
= options
.bufferSize
;
205 Connection
.prototype.setOptions = function(options
) {
206 this.setClientOptions_(options
);
207 var serviceOptions
= getServiceOptions(options
);
208 if ($Object
.keys(serviceOptions
).length
== 0)
210 return this.remoteConnection_
.setOptions(serviceOptions
).then(
212 return !!result
.success
;
213 }).catch(function() {
218 Connection
.prototype.getControlSignals = function() {
219 return this.remoteConnection_
.getControlSignals().then(function(result
) {
221 throw new Error('Failed to get control signals.');
222 var signals
= result
.signals
;
232 Connection
.prototype.setControlSignals = function(signals
) {
233 var controlSignals
= {};
234 if ('dtr' in signals
) {
235 controlSignals
.has_dtr
= true;
236 controlSignals
.dtr
= signals
.dtr
;
238 if ('rts' in signals
) {
239 controlSignals
.has_rts
= true;
240 controlSignals
.rts
= signals
.rts
;
242 return this.remoteConnection_
.setControlSignals(controlSignals
).then(
244 return !!result
.success
;
248 Connection
.prototype.flush = function() {
249 return this.remoteConnection_
.flush().then(function(result
) {
250 return !!result
.success
;
254 Connection
.prototype.setPaused = function(paused
) {
255 this.paused_
= paused
;
258 var connections_
= {};
259 var nextConnectionId_
= 0;
261 // Wrap all access to |connections_| through getConnections to avoid adding
262 // any synchronous dependencies on it. This will likely be important when
263 // supporting persistent connections by stashing them.
264 function getConnections() {
265 return Promise
.resolve(connections_
);
268 function getConnection(id
) {
269 return getConnections().then(function(connections
) {
270 if (!connections
[id
])
271 throw new Error ('Serial connection not found.');
272 return connections
[id
];
276 function allocateConnectionId() {
277 return Promise
.resolve(nextConnectionId_
++);
281 getDevices
: getDevices
,
282 createConnection
: Connection
.create
,
283 getConnection
: getConnection
,
284 getConnections
: getConnections
,