libnfc-d

git clone git://xatko.vsos.ethz.ch/libnfc-d.git
Log | Files | Refs

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 }