package.d (7883B)
1 /** A D-based wrapper of some libnfc-functionality 2 * 3 * Examples: 4 * ------------- 5 * import nfc; 6 * 7 * writeln("libnfc version ", nfcVersion()); 8 * 9 * Context c=new Context(); 10 * 11 * auto d = c.open(); 12 * d.init(); 13 * 14 * writeln("Device name: ", d.name); 15 * 16 * Target t; 17 * t.baud = t.Baud.B106; 18 * auto params = &t.ISO14443A(); 19 * 20 * d.selectPassive(t); 21 * writeln("The following ISO14443A tag was found: ", t.toString()); 22 * ------------- 23 */ 24 25 module nfc; 26 import nfc.c; 27 import std.string; 28 import std.meta; 29 import std.conv:to; 30 import std.traits; 31 import std.exception; 32 import std.array : Appender; 33 import core.time : Duration, dur; 34 import std.algorithm.mutation:copy; 35 36 37 private auto toConnString(Range)(Range r) if(isSomeString!Range){ 38 nfc_connstring cs; 39 r.copy(cs[]); 40 return cs; 41 } 42 43 /** Return the libnfc version string 44 */ 45 string nfcVersion(){ 46 return cast(string)(nfc_version().fromStringz()); 47 } 48 49 unittest{ 50 assert(nfcVersion().length > 0); 51 } 52 53 class NfcException : Exception{ 54 this(A...)(A a){ 55 super(a); 56 } 57 } 58 59 /** The main class encapsulating the libnfc context 60 * 61 * This class needs to be instantiated before doing anything else. 62 * It can split off devices by using Context.open 63 */ 64 65 class Context{ 66 nfc_context *ctx; 67 68 this(){ 69 nfc_init(&ctx); 70 if(ctx is null){ 71 throw new Exception("Malloc"); 72 } 73 } 74 75 ~this(){ 76 nfc_exit(ctx); 77 ctx = null; 78 } 79 80 /** Open the default device on the system 81 */ 82 Device open(){ 83 return new Device(enforce!NfcException(nfc_open(ctx, cast(char*)null), "Could not open default NFC device")); 84 } 85 86 /** Open a device given a connection string 87 */ 88 Device open(string connstring){ 89 return new Device(enforce!NfcException(nfc_open(ctx, connstring.toConnString.ptr), "Could not open NFC device "~connstring)); 90 } 91 } 92 93 unittest{ 94 Context c = new Context(); 95 } 96 97 /** The class denoting the target of a NFC device 98 * 99 * This encapsulates both the modulation and target struct of libnfc. 100 * The modulation can be set by the .baud property, the modulation type 101 * by calling the corresponding function (e.g. for ISO14443A t.ISO14443A()). 102 * This call additionally returns a reference to the configuration struct. 103 * 104 * For all the possibilities, refer to the struct nfc_modulation_type 105 */ 106 struct Target{ 107 private nfc_target nt; 108 109 alias nt this; 110 111 static private string modulationName(string name)(){ 112 return name[4..$-5].toUpper(); 113 } 114 115 static private string modulationType(string name)(){ 116 return "NMT_"~modulationName!name; 117 } 118 119 template ModulationType(string name){ 120 alias ModulationType = mixin("typeof(nt.nti."~name~")"); 121 } 122 123 static private string funcDef(string name)(){ 124 alias T = ModulationType!name; 125 return "ref "~modulationName!(T.stringof)~"(){ 126 nt.nm.nmt = nfc_modulation_type."~modulationType!(T.stringof)~"; 127 return nt.nti."~name~"; 128 }"; 129 } 130 131 static foreach(T; FieldNameTuple!(typeof(nt.nti))){ 132 mixin(funcDef!T); 133 } 134 135 /** 136 * The possible baud rates for communication 137 */ 138 enum Baud{ 139 UNDEF = nfc_baud_rate.NBR_UNDEFINED, 140 B106 = nfc_baud_rate.NBR_106, 141 B212 = nfc_baud_rate.NBR_212, 142 B424 = nfc_baud_rate.NBR_424, 143 B847 = nfc_baud_rate.NBR_847, 144 } 145 146 /** 147 * Returns the baud rate 148 */ 149 Baud baud() const{ 150 return cast(Baud) nt.nm.nbr; 151 } 152 153 /** 154 * Sets the baud rate 155 */ 156 Baud baud(Baud b){ 157 nt.nm.nbr = cast(nfc_baud_rate)b; 158 return baud(); 159 } 160 161 /** 162 * Returns the modulation struct 163 */ 164 auto modulation(){ 165 return nt.nm; 166 } 167 168 /** 169 * Returns a pointer to the c target struct 170 */ 171 auto ptr(){ 172 return &nt; 173 } 174 175 /** 176 * Prints the internal variables independant of the modulation type 177 */ 178 void toString(scope void delegate(const(char)[]) sink) const{ 179 string card = nt.nm.nmt.to!string; 180 string baud = baud().to!string; 181 sink(card[4..$]); 182 sink(", Baud "); 183 sink(baud); 184 185 void printer(T)(T t){ 186 foreach(mem; FieldNameTuple!T){ 187 auto r = mixin("t."~mem); 188 sink("\n\t"); 189 sink(mem); 190 sink(": "); 191 sink(r.to!string); 192 } 193 194 } 195 196 lbrk:switch(nt.nm.nmt){ 197 static foreach(name; FieldNameTuple!(typeof(nt.nti))){ 198 mixin(`case nfc_modulation_type.`~modulationType!((ModulationType!name).stringof)~`: 199 printer(nt.nti.`~name~`); 200 break lbrk;`); 201 } 202 default: 203 sink("Not implemented"); 204 break; 205 } 206 } 207 208 string toString() const{ 209 Appender!string app; 210 toString(x=>app~=x); 211 return app.data; 212 } 213 } 214 215 /** A nfc reader/writer device 216 * 217 * This is typically instantiated via the Context class, using Context.open 218 */ 219 class Device{ 220 private nfc_device *dev; 221 222 223 /** Exception thrown on error 224 */ 225 class NfcException : Exception{ 226 this(string msg, string file=__FILE__, size_t line = __LINE__, Throwable next=null){ 227 super(msg~": "~error(), file, line, next); 228 } 229 } 230 231 private int check(int retval, string msg="Call to libnfc failed", string file=__FILE__, size_t line=__LINE__){ 232 if(retval < NFC.SUCCESS){ 233 throw new NfcException(msg, file, line); 234 } 235 return retval; 236 } 237 238 package this(nfc_device *dev){ 239 assert(dev !is null); 240 this.dev = dev; 241 } 242 243 ~this(){ 244 nfc_close(dev); 245 dev = null; 246 } 247 248 /** Returns the device name 249 */ 250 string name(){ 251 auto s = nfc_device_get_name(dev); 252 return cast(string)(s.fromStringz()); 253 } 254 255 /** Initializes the initiator 256 * 257 * Params: 258 * secure = Whether to initialize a secure element or not 259 */ 260 void init(bool secure = false){ 261 if(secure){ 262 check(nfc_initiator_init_secure_element(dev)); 263 } 264 else{ 265 check(nfc_initiator_init(dev)); 266 } 267 } 268 269 /** Returns a list of at most ntargets targets found by the device 270 */ 271 Target[] listPassiveTargets(Target t, int ntargets){ 272 return listPassiveTargets(t, new Target[ntargets]); 273 } 274 275 /** Reads all targets found by the device into the given array 276 */ 277 Target[] listPassiveTargets(Target t, Target[] targets){ 278 auto ret = check(nfc_initiator_list_passive_targets(dev, t.modulation, cast(nfc_target*)targets.ptr, targets.length)); 279 return targets[0..ret]; 280 } 281 282 /** Checks whether a target is still present 283 * 284 * The target needs to be selected prior to calling this function 285 */ 286 bool targetIsPresent(Target t){ 287 auto ret = nfc_initiator_target_is_present(dev, t.ptr); 288 return ret == NFC.SUCCESS; 289 } 290 291 /** Transmits the bytes in send and reads the bytes in receive 292 * 293 * Params: 294 * send = The bytes to send, or null if none 295 * receive = The bytes to receive, or null if none 296 * timeout = The time to wait for the response, infinite if zero 297 */ 298 ubyte[] transceive(ubyte[] send, ubyte[] receive = null, Duration timeout=Duration.zero){ 299 int ret = check(nfc_initiator_transceive_bytes(dev, 300 send.ptr, 301 send.length, 302 receive.ptr, 303 receive.length, 304 cast(int)timeout.total!"msecs" 305 )); 306 307 return receive[0..ret]; 308 } 309 310 /** Selects a device for communication 311 */ 312 int selectPassive(ref Target t, ubyte[] initData = null){ 313 int ret = check(nfc_initiator_select_passive_target(dev, t.modulation, initData.ptr, initData.length, t.ptr)); 314 return ret; 315 } 316 317 /** Polls for devices, and returns the number of devices found, as 318 * well as one of them in t 319 * 320 * Params: 321 * modulations = an array of modulations to try 322 * t = The first found target 323 * polling = specifies the number of polling (0x01 - 0xFE: 1 up to 254 324 * polling, 0xFF: Endless polling) 325 * period = indicates the polling period, 150ms - 2.25s 326 * 327 * Returns: 328 * Polled targets count 329 */ 330 int poll(nfc_modulation[] modulations, out Target t, ubyte polling=0xFF, Duration period=dur!"seconds"(1)){ 331 auto period_byte = period.total!"msecs"/150; 332 assert(period_byte >= 1 && period_byte <= 0xF); 333 int ret = check(nfc_initiator_poll_target(dev, modulations.ptr, modulations.length, polling, cast(ubyte)period_byte, t.ptr)); 334 return ret; 335 } 336 337 /** Returns the last error from the device */ 338 private string error() const{ 339 auto s = nfc_strerror(dev); 340 return cast(string)(s.fromStringz()); 341 } 342 }