-
Notifications
You must be signed in to change notification settings - Fork 0
/
pp.m
323 lines (289 loc) · 10.4 KB
/
pp.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
function input = pp(pins,output,slowChecks,port,addr)
% [input] = pp(pins,[output],[slowChecks],[port],[address])
%
% read/write TTL levels on parallel port pins under linux, win32 (using
% porttalk), or matlab32 on win64 (using io32)
%
% Arguments:
% pins integer vector of hardware pin nums
% output logical vector same size as pins indicating TTL levels to
% write -- omit or use [] to merely read
% slowChecks optional logical scalar (default true)
% indicates whether to run extensive and slow input
% validation, checking all relevant OS information
% regarding the ports (that i could find)
% if set to false when writing to incorrect addr, you could damage your OS
% port optional integer vector indicating parallel port indices
% defaults to first port found in /proc/sys/dev/parport/
% (set to [] for windows)
% address optional integer vector indicating hardware memory address
% corresponding to ports.
% eg, the first value in /proc/sys/dev/parport/parport0/base-addr
% (must be scalar on windows -- hex2dec the first I/O Range address on the
% Resources tab of the LPT entry in the device manager)
%
% Returns: logical matrix of values read (rows correspond to pins, columns
% to ports) -- use no return value to merely write
%
% if multiple ports/addrs are supplied, the same output/pins are used for each.
%
% if you want pp to be as fast as possible, supply both the port
% and addr, and set slowChecks to false.
%
% the parallel port's pins are divided into three registers, and some pins
% are logically inverted. pp takes care of these details for you.
%
% you cannot write to the status register (pins 10, 11-13, 15).
% values read from the data register (pins 2-9) are likely the values
% you wrote, not input from external equipment. use the status or control
% register (pins 1, 14, 16-17) to read input.
%
% pp works for built-in as well as add-on (PCI/PCMCIA) parallel ports
% if their drivers are installed. on fedora 15, i found both to be
% installed by default.
%
% pp requires the corresponding compiled mex file (ppMex, lptread/lptwrite, io32).
% you may need to recompile it for your arch -- see instructions in ppMex.c, etc.
% for fastest operation, you could call ppMex directly, but it does no input
% validation, and depends on formatting provided by getPinInfo as performed
% in this file.
%
% ppMex is currently configured to use iopl/inb/outb, which require root
% (ie, you need to start matlab with sudo). we may someday switch to ppdev,
% which is not supposed to require root, but the current prototype ppdev code
% seems to need it to read /dev/parport* anyway.
%
% Examples:
% vals = pp(uint8([1 2 10 3])) % read some pins (slow)
% vals = pp(uint8([1 2 10 3]),[],false,uint8(0),uint64(888)) % read some pins (fast)
%
% pp(uint8([16 4 8 1]),[true false true true]) % write to some pins (slow)
% pp(uint8([16 4 8 1]),[true false true true],true, uint8(0),uint64(888)) % write to some pins (slow -- validates port address for safety)
% pp(uint8([16 4 8 1]),[true false t``rue true],false,uint8(0),uint64(888)) % write to some pins (fast -- dangerous if addr is incorrect)
%
% vals = pp(uint8([16 4 8 1]),[true false true true]) % write some pins, then immediately read from them, hopefully verifying what you wrote
%
% Copyright (C) 2011 Erik Flister, University of Oregon, erik.flister <at> gmail
if ~exist('slowChecks','var') || isempty(slowChecks)
slowChecks=true;
end
if ~exist('output','var')
output = logical([]);
end
if isunix && ~ismac %IsLinux probably more appropriate, but requires psychtoolbox
useSscanf=true; %maybe textscan is faster?
pportDir='/proc/sys/dev/parport/';
if slowChecks
[s dmesg]=unix('dmesg | grep parport');
if s~=0
error('couldn''t dmesg')
end
[s dev]=unix('ls -al /dev | grep parport');
if s~=0
error('couldn''t dev')
end
[s drv]=unix('grep ppdev /proc/devices');
if s~=0
error('couldn''t proc')
end
[s lsm]=unix('lsmod | grep ppdev');
if s~=0
error('couldn''t lsmod ppdev')
end
[s lsmp]=unix('lsmod | grep parport');
if s~=0
error('couldn''t lsmod parport')
end
[s iop]=unix('cat /proc/ioports | grep parport');
if s~=0
error('couldn''t ioports')
end
end
if slowChecks || ~exist('port','var') || isempty(port)
ports=uint8([]);
d=dir(pportDir);
for i=1:length(d)
if d(i).isdir
if useSscanf
[A, count, errmsg] = sscanf(d(i).name, 'parport%u');
if count==1 && isempty(errmsg) && ~isempty(A)
ports(end+1)=uint8(A);
end
else
C = textscan(d(i).name, 'parport%u8');
if ~isempty(C{1})
ports(end+1)=C{1};
end
end
end
end
if ~exist('port','var') || isempty(port)
port=ports(1);
end
if ~isvector(port) || ~isinteger(port) || ~all(port>=0) || ~all(ismember(port,ports)) || length(unique(port))<length(port)
error('port must be integer vector of valid port IDs with no duplicates')
end
ports=intersect(port,ports);
if length(ports)<length(port)
error('couldn''t find all ports')
end
else
ports=port;
end
if ~exist('addr','var') || isempty(addr)
addr=zeros(size(port),'uint64');
end
if slowChecks
if ~isvector(addr) || ~isinteger(addr) || ~all(addr>=0) || ~all(size(addr)==size(port))
error('addr must be integer vector >=0 same size as port')
end
end
for i=1:length(ports)
name = ['parport' num2str(ports(i))];
base = ['cat ' pportDir name filesep];
if slowChecks
[s modes]=unix([base 'modes']);
if s~=0
error('couldn''t cat modes')
end
if isempty(strfind(modes,'SPP'))
error('SPP not supported')
end
if isempty(strfind(dev,name)) || isempty(strfind(dmesg,name))
error('pport in pportDir but not dev or dmesg')
end
end
if addr(i)==0 || slowChecks
[s a]=unix([base 'base-addr']);
if s~=0
error('couldn''t cat base-addr')
end
if useSscanf
[a, count, errmsg] = sscanf(a, '%u %*u');
% the second number is always 0 for me (ubuntu 11/fedora 15),
% but kkd reported a 1912 on parport0 for his machine (ubuntu 12.04).
% according to http://www.kernel.org/doc/Documentation/parport.txt
% this is just another address for the port? what's it for?
if count==1 && isempty(errmsg) && ~isempty(a)
a=uint64(a);
else
a
count
errmsg
[s a]=unix([base 'base-addr'])
error('bad addr')
end
else
a = textscan(a, '%u64 %*u64');
if ~isempty(a{1})
a=a{1};
else
error('bad addr')
end
end
if addr(i)==0
addr(i)=a;
else
if addr(i)~=a
error('port and address don''t match')
end
end
end
end
else
error('only linux and windows supported')
end
if isempty(pins)
input = [];
return
end
if slowChecks
if ~isvector(pins) || ~isinteger(pins) || ~all(pins>0) || ~all(pins<=17) || length(unique(pins))<length(pins)
error('pins must be integer vector 1<=pins<=17 with no duplicates')
end
if ~(isempty(output) || (islogical(output) && all(size(output)==size(pins))))
error('output must be empty or logical vector same size as pins')
end
end
bitSpecs=getPinInfo(pins);
inds = logical(bitSpecs(:,3));
function x = inv (x)
x(inds) = ~x(inds);
end
if ~isempty(output)
output = inv(output);
end
if ispc
if nargout>0
input = nan(length(pins),1);
end
[offsets, ~, vInds] = unique(bitSpecs(:,2));
for i = 1:length(offsets)
a = addr + double(offsets(i));
these = vInds == i;
cInds = 8 - bitSpecs(these,1);
if isnan(ioObj)
codeStr = lptread(a);
else
codeStr = double(io32(ioObj,a));
end
codeStr = fastDec2Bin(codeStr);
if isempty(output)
if slowChecks && ~all(isnan(input(these)))
error('whoops')
end
input(these) = codeStr(cInds)=='1';
elseif nargout == 0
if offsets(i)==1
error('cannot write to status register')
end
codeStr(cInds) = char('0'*ones(1,sum(these)) + output(these)*('1' - '0'));
codeStr = fastBin2Dec(codeStr);
if isnan(ioObj)
lptwrite(a,codeStr);
else
io32(ioObj,a,codeStr);
end
else
error('doesn''t yet support write validation')
end
end
if nargout>0
if slowChecks && any(isnan(input))
error('whoops')
end
input = inv(input);
end
else
% w=warning('off', 'MATLAB:concatenation:integerInteraction');
input = inv(ppMex([uint64(ports(:)) uint64(addr(:))],[bitSpecs(:,1:2) uint8(output(:))]));
% warning(w.state, 'MATLAB:concatenation:integerInteraction');
end
end
function out=getPinInfo(pinNum)
pportSpec=uint8([... %[bitNum,regOffset,inv]
0 2 1; %1
0 0 0; %2
1 0 0; %3
2 0 0; %4
3 0 0; %5
4 0 0; %6
5 0 0; %7
6 0 0; %8
7 0 0; %9
6 1 0; %10
7 1 1; %11
5 1 0; %12
4 1 0; %13
1 2 1; %14
3 1 0; %15
2 2 0; %16
3 2 1; %17
]);
try
out=pportSpec(pinNum,:);
catch e
e
error('pin num must be integer 1-17')
end
end