src: improve SCardConnect interface

- We can optionally pass the share_mode and preferred_protocol options. They are
  optional to keep backwards compatibility.
- Update README
- Define constants in C++ land.
This commit is contained in:
Santiago Gimeno
2014-07-29 12:15:32 +02:00
parent c5d09f8b19
commit 279bc112ce
4 changed files with 92 additions and 27 deletions

View File

@@ -108,25 +108,28 @@ Emitted when the card reader has been removed.
#### Event: 'status'
* *status* `Object`.
* *state* The current status of the card reader as returned by `SCardGetStatusChange`
* *state* The current status of the card reader as returned by `[SCardGetStatusChange](http://pcsclite.alioth.debian.org/pcsc-lite/node20.html)`
* *atr* ATR of the card inserted (if any)
Emitted whenever the status of the reader changes.
#### reader.connect(callback)
#### reader.connect([options], callback)
* *options* `Object` Optional
* *share_mode* `Number` Shared mode. Defaults to `SCARD_SHARE_EXCLUSIVE`
* *protocol* `Number` Preferred protocol. Defaults to `SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1`
* *callback* `Function` called when connection operation ends
* *error* `Error`
* *protocol* `Number` Established protocol to this connection.
Wrapper around `SCardConnect`. Establishes a connection to the reader.
Wrapper around `[SCardConnect](http://pcsclite.alioth.debian.org/pcsc-lite/node12.html)`. Establishes a connection to the reader.
#### reader.disconnect(callback)
* *callback* `Function` called when disconnection operation ends
* *error* `Error`
Wrapper around `SCardDisconnect`. Terminates a connection to the reader.
Wrapper around `[SCardDisconnect](http://pcsclite.alioth.debian.org/pcsc-lite/node14.html)`. Terminates a connection to the reader.
#### reader.transmit(input, res_len, protocol, callback)
@@ -137,7 +140,7 @@ Wrapper around `SCardDisconnect`. Terminates a connection to the reader.
* *error* `Error`
* *output* `Buffer`
Wrapper around `SCardTransmit`. Sends an APDU to the smart card contained in the reader connected to.
Wrapper around `[SCardTransmit](http://pcsclite.alioth.debian.org/pcsc-lite/node17.html)`. Sends an APDU to the smart card contained in the reader connected to.
#### reader.control(input, control_code, res_len, callback)
@@ -148,4 +151,4 @@ Wrapper around `SCardTransmit`. Sends an APDU to the smart card contained in the
* *error* `Error`
* *output* `Buffer`
Wrapper around `SCardControl`. Sends a command directly to the IFD Handler (reader driver) to be processed by the reader.
Wrapper around `[SCardControl](http://pcsclite.alioth.debian.org/pcsc-lite/node18.html)`. Sends a command directly to the IFD Handler (reader driver) to be processed by the reader.

View File

@@ -67,10 +67,18 @@ module.exports = function() {
return p;
};
CardReader.prototype.connect = function(cb) {
CardReader.prototype.connect = function(options, cb) {
if (typeof options === 'function') {
cb = options;
options = undefined;
}
options = options || {};
options.share_mode = options.share_mode || this.SCARD_SHARE_EXCLUSIVE;
options.protocol = options.protocol || this.SCARD_PROTOCOL_T0 | this.SCARD_PROTOCOL_T1;
if (!this.connected) {
this._connect(cb);
this._connect(options.share_mode, options.protocol, cb);
} else {
cb();
}
@@ -109,18 +117,6 @@ CardReader.prototype.control = function(data, control_code, res_len, cb) {
});
};
CardReader.prototype.SCARD_STATE_UNAWARE = 0x0000;
CardReader.prototype.SCARD_STATE_IGNORE = 0x0001;
CardReader.prototype.SCARD_STATE_CHANGED = 0x0002;
CardReader.prototype.SCARD_STATE_UNKNOWN = 0x0004;
CardReader.prototype.SCARD_STATE_UNAVAILABLE = 0x0008;
CardReader.prototype.SCARD_STATE_EMPTY = 0x0010;
CardReader.prototype.SCARD_STATE_PRESENT = 0x0020;
CardReader.prototype.SCARD_STATE_ATRMATCH = 0x0040;
CardReader.prototype.SCARD_STATE_EXCLUSIVE = 0x0080;
CardReader.prototype.SCARD_STATE_INUSE = 0x0100;
CardReader.prototype.SCARD_STATE_MUTE = 0x0200;
// extend prototype
function inherits(target, source) {
for (var k in source.prototype) {

View File

@@ -30,6 +30,46 @@ void CardReader::init(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(tpl, "_control", Control);
NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close);
// PCSCLite constants
// Share Mode
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_SHARE_SHARED"),
Integer::New(SCARD_SHARE_SHARED));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_SHARE_EXCLUSIVE"),
Integer::New(SCARD_SHARE_EXCLUSIVE));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_SHARE_DIRECT"),
Integer::New(SCARD_SHARE_DIRECT));
// Protocol
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_PROTOCOL_T0"),
Integer::New(SCARD_PROTOCOL_T0));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_PROTOCOL_T1"),
Integer::New(SCARD_PROTOCOL_T1));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_PROTOCOL_RAW"),
Integer::New(SCARD_PROTOCOL_RAW));
// State
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_UNAWARE"),
Integer::New(SCARD_STATE_UNAWARE));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_IGNORE"),
Integer::New(SCARD_STATE_IGNORE));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_CHANGED"),
Integer::New(SCARD_STATE_CHANGED));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_UNKNOWN"),
Integer::New(SCARD_STATE_UNKNOWN));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_UNAVAILABLE"),
Integer::New(SCARD_STATE_UNAVAILABLE));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_EMPTY"),
Integer::New(SCARD_STATE_EMPTY));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_PRESENT"),
Integer::New(SCARD_STATE_PRESENT));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_ATRMATCH"),
Integer::New(SCARD_STATE_ATRMATCH));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_EXCLUSIVE"),
Integer::New(SCARD_STATE_EXCLUSIVE));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_INUSE"),
Integer::New(SCARD_STATE_INUSE));
tpl->PrototypeTemplate()->Set(String::NewSymbol("SCARD_STATE_MUTE"),
Integer::New(SCARD_STATE_MUTE));
constructor = Persistent<Function>::New(tpl->GetFunction());
target->Set(String::NewSymbol("CardReader"), constructor);
}
@@ -89,18 +129,33 @@ Handle<Value> CardReader::Connect(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsFunction()) {
// The second argument is the length of the data to be received
if (!args[0]->IsUint32()) {
return ThrowException(Exception::TypeError(
String::New("First argument must be a callback function")));
String::New("First argument must be an integer")));
}
Local<Function> cb = Local<Function>::Cast(args[0]);
if (!args[1]->IsUint32()) {
return ThrowException(Exception::TypeError(
String::New("Second argument must be an integer")));
}
if (!args[2]->IsFunction()) {
return ThrowException(Exception::TypeError(
String::New("Third argument must be a callback function")));
}
ConnectInput* ci = new ConnectInput();
ci->share_mode = args[0]->Uint32Value();
ci->pref_protocol = args[1]->Uint32Value();
Local<Function> cb = Local<Function>::Cast(args[2]);
// This creates our work request, including the libuv struct.
Baton* baton = new Baton();
baton->request.data = baton;
baton->callback = Persistent<Function>::New(cb);
baton->reader = ObjectWrap::Unwrap<CardReader>(args.This());
baton->input = ci;
// Schedule our work request with libuv. Here you can specify the functions
// that should be executed in the threadpool and back in the main thread
@@ -333,6 +388,7 @@ void* CardReader::HandlerFunction(void* arg) {
void CardReader::DoConnect(uv_work_t* req) {
Baton* baton = static_cast<Baton*>(req->data);
ConnectInput *ci = static_cast<ConnectInput*>(baton->input);
unsigned long card_protocol;
LONG result = SCARD_S_SUCCESS;
@@ -347,9 +403,12 @@ void CardReader::DoConnect(uv_work_t* req) {
/* Connect */
if (result == SCARD_S_SUCCESS) {
result = SCardConnect(obj->m_card_context, obj->m_name.c_str(),
SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&obj->m_card_handle, &card_protocol);
result = SCardConnect(obj->m_card_context,
obj->m_name.c_str(),
ci->share_mode,
ci->pref_protocol,
&obj->m_card_handle,
&card_protocol);
}
/* Unlock the mutex */
@@ -372,6 +431,7 @@ void CardReader::AfterConnect(uv_work_t* req) {
HandleScope scope;
Baton* baton = static_cast<Baton*>(req->data);
ConnectInput *ci = static_cast<ConnectInput*>(baton->input);
ConnectResult *cr = static_cast<ConnectResult*>(baton->result);
if (cr->result) {
@@ -393,6 +453,7 @@ void CardReader::AfterConnect(uv_work_t* req) {
// The callback is a permanent handle, so we have to dispose of it manually.
baton->callback.Dispose();
delete ci;
delete cr;
delete baton;
}

View File

@@ -21,9 +21,14 @@ class CardReader: public node::ObjectWrap {
void *result;
};
struct ConnectInput {
DWORD share_mode;
DWORD pref_protocol;
};
struct ConnectResult {
LONG result;
unsigned long card_protocol;
DWORD card_protocol;
};
struct TransmitInput {