1*d5c9a868SElliott Hughes /* Copyright 1994,1996-2003,2005,2007,2009 Alain Knaff.
2*d5c9a868SElliott Hughes * This file is part of mtools.
3*d5c9a868SElliott Hughes *
4*d5c9a868SElliott Hughes * Mtools is free software: you can redistribute it and/or modify
5*d5c9a868SElliott Hughes * it under the terms of the GNU General Public License as published by
6*d5c9a868SElliott Hughes * the Free Software Foundation, either version 3 of the License, or
7*d5c9a868SElliott Hughes * (at your option) any later version.
8*d5c9a868SElliott Hughes *
9*d5c9a868SElliott Hughes * Mtools is distributed in the hope that it will be useful,
10*d5c9a868SElliott Hughes * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*d5c9a868SElliott Hughes * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12*d5c9a868SElliott Hughes * GNU General Public License for more details.
13*d5c9a868SElliott Hughes *
14*d5c9a868SElliott Hughes * You should have received a copy of the GNU General Public License
15*d5c9a868SElliott Hughes * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
16*d5c9a868SElliott Hughes *
17*d5c9a868SElliott Hughes * Io to an xdf disk
18*d5c9a868SElliott Hughes *
19*d5c9a868SElliott Hughes * written by:
20*d5c9a868SElliott Hughes *
21*d5c9a868SElliott Hughes * Alain L. Knaff
22*d5c9a868SElliott Hughes * [email protected]
23*d5c9a868SElliott Hughes *
24*d5c9a868SElliott Hughes */
25*d5c9a868SElliott Hughes
26*d5c9a868SElliott Hughes
27*d5c9a868SElliott Hughes #include "sysincludes.h"
28*d5c9a868SElliott Hughes #ifdef OS_linux
29*d5c9a868SElliott Hughes #include "msdos.h"
30*d5c9a868SElliott Hughes #include "mtools.h"
31*d5c9a868SElliott Hughes #include "devices.h"
32*d5c9a868SElliott Hughes #include "xdf_io.h"
33*d5c9a868SElliott Hughes
34*d5c9a868SElliott Hughes /* Algorithms can't be patented */
35*d5c9a868SElliott Hughes
36*d5c9a868SElliott Hughes typedef struct sector_map {
37*d5c9a868SElliott Hughes unsigned int head:1;
38*d5c9a868SElliott Hughes unsigned int size:7;
39*d5c9a868SElliott Hughes } sector_map_t;
40*d5c9a868SElliott Hughes
41*d5c9a868SElliott Hughes
42*d5c9a868SElliott Hughes static struct {
43*d5c9a868SElliott Hughes unsigned char track_size;
44*d5c9a868SElliott Hughes unsigned int track0_size:7;
45*d5c9a868SElliott Hughes unsigned int rootskip:1;
46*d5c9a868SElliott Hughes unsigned char rate;
47*d5c9a868SElliott Hughes sector_map_t map[9];
48*d5c9a868SElliott Hughes } xdf_table[]= {
49*d5c9a868SElliott Hughes {
50*d5c9a868SElliott Hughes 19, 16, 0, 0,
51*d5c9a868SElliott Hughes { {0,3}, {0,6}, {1,2}, {0,2}, {1,6}, {1,3}, {0,0} }
52*d5c9a868SElliott Hughes },
53*d5c9a868SElliott Hughes {
54*d5c9a868SElliott Hughes 23, 19, 0, 0,
55*d5c9a868SElliott Hughes { {0,3}, {0,4}, {1,6}, {0,2}, {1,2}, {0,6}, {1,4}, {1,3}, {0,0} }
56*d5c9a868SElliott Hughes },
57*d5c9a868SElliott Hughes {
58*d5c9a868SElliott Hughes 46, 37, 1, 0x43,
59*d5c9a868SElliott Hughes { {0,3}, {0,4}, {0,5}, {0,7}, {1,3}, {1,4}, {1,5}, {1,7}, {0,0} }
60*d5c9a868SElliott Hughes },
61*d5c9a868SElliott Hughes {
62*d5c9a868SElliott Hughes 24, 20, 1, 0,
63*d5c9a868SElliott Hughes { {0,5}, {1,6}, {0,6}, {1, 5} }
64*d5c9a868SElliott Hughes },
65*d5c9a868SElliott Hughes {
66*d5c9a868SElliott Hughes 48, 41, 1, 0,
67*d5c9a868SElliott Hughes { {0,6}, {1,7}, {0,7}, {1, 6} }
68*d5c9a868SElliott Hughes }
69*d5c9a868SElliott Hughes };
70*d5c9a868SElliott Hughes
71*d5c9a868SElliott Hughes #define NUMBER(x) (sizeof(x)/sizeof(x[0]))
72*d5c9a868SElliott Hughes
73*d5c9a868SElliott Hughes typedef struct {
74*d5c9a868SElliott Hughes unsigned char begin; /* where it begins */
75*d5c9a868SElliott Hughes unsigned char end;
76*d5c9a868SElliott Hughes unsigned char sector;
77*d5c9a868SElliott Hughes unsigned char sizecode;
78*d5c9a868SElliott Hughes
79*d5c9a868SElliott Hughes unsigned int dirty:1;
80*d5c9a868SElliott Hughes unsigned int phantom:2;
81*d5c9a868SElliott Hughes unsigned int valid:1;
82*d5c9a868SElliott Hughes unsigned int head:1;
83*d5c9a868SElliott Hughes } TrackMap_t;
84*d5c9a868SElliott Hughes
85*d5c9a868SElliott Hughes
86*d5c9a868SElliott Hughes
87*d5c9a868SElliott Hughes typedef struct Xdf_t {
88*d5c9a868SElliott Hughes struct Stream_t head;
89*d5c9a868SElliott Hughes
90*d5c9a868SElliott Hughes int fd;
91*d5c9a868SElliott Hughes char *buffer;
92*d5c9a868SElliott Hughes
93*d5c9a868SElliott Hughes bool track_valid;
94*d5c9a868SElliott Hughes uint8_t current_track;
95*d5c9a868SElliott Hughes
96*d5c9a868SElliott Hughes sector_map_t *map;
97*d5c9a868SElliott Hughes
98*d5c9a868SElliott Hughes uint32_t track_size;
99*d5c9a868SElliott Hughes int track0_size;
100*d5c9a868SElliott Hughes uint16_t sector_size;
101*d5c9a868SElliott Hughes uint8_t FatSize;
102*d5c9a868SElliott Hughes uint16_t RootDirSize;
103*d5c9a868SElliott Hughes TrackMap_t *track_map;
104*d5c9a868SElliott Hughes
105*d5c9a868SElliott Hughes unsigned char last_sector;
106*d5c9a868SElliott Hughes unsigned char rate;
107*d5c9a868SElliott Hughes
108*d5c9a868SElliott Hughes unsigned int stretch:1;
109*d5c9a868SElliott Hughes unsigned int rootskip:1;
110*d5c9a868SElliott Hughes signed int drive:4;
111*d5c9a868SElliott Hughes } Xdf_t;
112*d5c9a868SElliott Hughes
113*d5c9a868SElliott Hughes typedef struct {
114*d5c9a868SElliott Hughes unsigned char head;
115*d5c9a868SElliott Hughes unsigned char sector;
116*d5c9a868SElliott Hughes unsigned char ptr;
117*d5c9a868SElliott Hughes } Compactify_t;
118*d5c9a868SElliott Hughes
119*d5c9a868SElliott Hughes
analyze_reply(RawRequest_t * raw_cmd,int do_print)120*d5c9a868SElliott Hughes static int analyze_reply(RawRequest_t *raw_cmd, int do_print)
121*d5c9a868SElliott Hughes {
122*d5c9a868SElliott Hughes int ret, bytes, newbytes;
123*d5c9a868SElliott Hughes
124*d5c9a868SElliott Hughes bytes = 0;
125*d5c9a868SElliott Hughes while(1) {
126*d5c9a868SElliott Hughes ret = analyze_one_reply(raw_cmd, &newbytes, do_print);
127*d5c9a868SElliott Hughes bytes += newbytes;
128*d5c9a868SElliott Hughes switch(ret) {
129*d5c9a868SElliott Hughes case 0:
130*d5c9a868SElliott Hughes return bytes;
131*d5c9a868SElliott Hughes case 1:
132*d5c9a868SElliott Hughes raw_cmd++;
133*d5c9a868SElliott Hughes break;
134*d5c9a868SElliott Hughes case -1:
135*d5c9a868SElliott Hughes if(bytes)
136*d5c9a868SElliott Hughes return bytes;
137*d5c9a868SElliott Hughes else
138*d5c9a868SElliott Hughes return 0;
139*d5c9a868SElliott Hughes }
140*d5c9a868SElliott Hughes }
141*d5c9a868SElliott Hughes }
142*d5c9a868SElliott Hughes
143*d5c9a868SElliott Hughes
144*d5c9a868SElliott Hughes
send_cmd(int fd,RawRequest_t * raw_cmd,int nr,const char * message,int retries)145*d5c9a868SElliott Hughes static int send_cmd(int fd, RawRequest_t *raw_cmd, int nr,
146*d5c9a868SElliott Hughes const char *message, int retries)
147*d5c9a868SElliott Hughes {
148*d5c9a868SElliott Hughes int j;
149*d5c9a868SElliott Hughes int ret=-1;
150*d5c9a868SElliott Hughes
151*d5c9a868SElliott Hughes if(!nr)
152*d5c9a868SElliott Hughes return 0;
153*d5c9a868SElliott Hughes for (j=0; j< retries; j++){
154*d5c9a868SElliott Hughes switch(send_one_cmd(fd, raw_cmd, message)) {
155*d5c9a868SElliott Hughes case -1:
156*d5c9a868SElliott Hughes return -1;
157*d5c9a868SElliott Hughes case 1:
158*d5c9a868SElliott Hughes j++;
159*d5c9a868SElliott Hughes continue;
160*d5c9a868SElliott Hughes case 0:
161*d5c9a868SElliott Hughes break;
162*d5c9a868SElliott Hughes }
163*d5c9a868SElliott Hughes if((ret=analyze_reply(raw_cmd, j)) > 0)
164*d5c9a868SElliott Hughes return ret; /* ok */
165*d5c9a868SElliott Hughes }
166*d5c9a868SElliott Hughes if(j > 1 && j == retries) {
167*d5c9a868SElliott Hughes fprintf(stderr,"Too many errors, giving up\n");
168*d5c9a868SElliott Hughes return 0;
169*d5c9a868SElliott Hughes }
170*d5c9a868SElliott Hughes return -1;
171*d5c9a868SElliott Hughes }
172*d5c9a868SElliott Hughes
173*d5c9a868SElliott Hughes
174*d5c9a868SElliott Hughes
175*d5c9a868SElliott Hughes #define REC (This->track_map[ptr])
176*d5c9a868SElliott Hughes #define END(x) (This->track_map[(x)].end)
177*d5c9a868SElliott Hughes #define BEGIN(x) (This->track_map[(x)].begin)
178*d5c9a868SElliott Hughes
add_to_request(Xdf_t * This,unsigned char ptr,RawRequest_t * request,int * nr,int direction,Compactify_t * compactify)179*d5c9a868SElliott Hughes static int add_to_request(Xdf_t *This, unsigned char ptr,
180*d5c9a868SElliott Hughes RawRequest_t *request, int *nr,
181*d5c9a868SElliott Hughes int direction, Compactify_t *compactify)
182*d5c9a868SElliott Hughes {
183*d5c9a868SElliott Hughes #if 0
184*d5c9a868SElliott Hughes if(direction == MT_WRITE) {
185*d5c9a868SElliott Hughes printf("writing %d: %u %d %d %d [%02x]\n",
186*d5c9a868SElliott Hughes ptr, This->current_track,
187*d5c9a868SElliott Hughes REC.head, REC.sector, REC.sizecode,
188*d5c9a868SElliott Hughes *(This->buffer + ptr * This->sector_size));
189*d5c9a868SElliott Hughes } else
190*d5c9a868SElliott Hughes printf(" load %d.%u\n", This->current_track, ptr);
191*d5c9a868SElliott Hughes #endif
192*d5c9a868SElliott Hughes if(REC.phantom) {
193*d5c9a868SElliott Hughes if(direction== MT_READ)
194*d5c9a868SElliott Hughes memset(This->buffer + ptr * This->sector_size, 0,
195*d5c9a868SElliott Hughes 128u << REC.sizecode);
196*d5c9a868SElliott Hughes return 0;
197*d5c9a868SElliott Hughes }
198*d5c9a868SElliott Hughes
199*d5c9a868SElliott Hughes if(*nr &&
200*d5c9a868SElliott Hughes RR_SIZECODE(request+(*nr)-1) == REC.sizecode &&
201*d5c9a868SElliott Hughes compactify->head == REC.head &&
202*d5c9a868SElliott Hughes compactify->ptr + 1 == ptr &&
203*d5c9a868SElliott Hughes compactify->sector +1 == REC.sector) {
204*d5c9a868SElliott Hughes RR_SETSIZECODE(request+(*nr)-1, REC.sizecode);
205*d5c9a868SElliott Hughes } else {
206*d5c9a868SElliott Hughes if(*nr)
207*d5c9a868SElliott Hughes RR_SETCONT(request+(*nr)-1);
208*d5c9a868SElliott Hughes RR_INIT(request+(*nr));
209*d5c9a868SElliott Hughes RR_SETDRIVE(request+(*nr), This->drive);
210*d5c9a868SElliott Hughes RR_SETRATE(request+(*nr), This->rate);
211*d5c9a868SElliott Hughes RR_SETTRACK(request+(*nr), This->current_track);
212*d5c9a868SElliott Hughes RR_SETPTRACK(request+(*nr),
213*d5c9a868SElliott Hughes This->current_track << This->stretch);
214*d5c9a868SElliott Hughes RR_SETHEAD(request+(*nr), REC.head);
215*d5c9a868SElliott Hughes RR_SETSECTOR(request+(*nr), REC.sector);
216*d5c9a868SElliott Hughes RR_SETSIZECODE(request+(*nr), REC.sizecode);
217*d5c9a868SElliott Hughes RR_SETDIRECTION(request+(*nr), direction);
218*d5c9a868SElliott Hughes RR_SETDATA(request+(*nr),
219*d5c9a868SElliott Hughes (caddr_t) This->buffer + ptr * This->sector_size);
220*d5c9a868SElliott Hughes (*nr)++;
221*d5c9a868SElliott Hughes }
222*d5c9a868SElliott Hughes compactify->ptr = ptr;
223*d5c9a868SElliott Hughes compactify->head = REC.head;
224*d5c9a868SElliott Hughes compactify->sector = REC.sector;
225*d5c9a868SElliott Hughes return 0;
226*d5c9a868SElliott Hughes }
227*d5c9a868SElliott Hughes
228*d5c9a868SElliott Hughes
add_to_request_if_invalid(Xdf_t * This,unsigned char ptr,RawRequest_t * request,int * nr,Compactify_t * compactify)229*d5c9a868SElliott Hughes static void add_to_request_if_invalid(Xdf_t *This, unsigned char ptr,
230*d5c9a868SElliott Hughes RawRequest_t *request, int *nr,
231*d5c9a868SElliott Hughes Compactify_t *compactify)
232*d5c9a868SElliott Hughes {
233*d5c9a868SElliott Hughes if(!REC.valid)
234*d5c9a868SElliott Hughes add_to_request(This, ptr, request, nr, MT_READ, compactify);
235*d5c9a868SElliott Hughes
236*d5c9a868SElliott Hughes }
237*d5c9a868SElliott Hughes
238*d5c9a868SElliott Hughes
adjust_bounds(Xdf_t * This,uint32_t ibegin,uint32_t iend,uint8_t * begin,uint8_t * end)239*d5c9a868SElliott Hughes static void adjust_bounds(Xdf_t *This, uint32_t ibegin, uint32_t iend,
240*d5c9a868SElliott Hughes uint8_t *begin, uint8_t *end)
241*d5c9a868SElliott Hughes {
242*d5c9a868SElliott Hughes /* translates begin and end from byte to sectors */
243*d5c9a868SElliott Hughes *begin = (uint8_t) (ibegin / This->sector_size);
244*d5c9a868SElliott Hughes *end = (uint8_t) ((iend + This->sector_size - 1) / This->sector_size);
245*d5c9a868SElliott Hughes }
246*d5c9a868SElliott Hughes
247*d5c9a868SElliott Hughes
try_flush_dirty(Xdf_t * This)248*d5c9a868SElliott Hughes static __inline__ int try_flush_dirty(Xdf_t *This)
249*d5c9a868SElliott Hughes {
250*d5c9a868SElliott Hughes unsigned char ptr;
251*d5c9a868SElliott Hughes int nr, bytes;
252*d5c9a868SElliott Hughes RawRequest_t requests[100];
253*d5c9a868SElliott Hughes Compactify_t compactify;
254*d5c9a868SElliott Hughes
255*d5c9a868SElliott Hughes if(!This->track_valid)
256*d5c9a868SElliott Hughes return 0;
257*d5c9a868SElliott Hughes
258*d5c9a868SElliott Hughes nr = 0;
259*d5c9a868SElliott Hughes for(ptr=0; ptr < This->last_sector; ptr=REC.end)
260*d5c9a868SElliott Hughes if(REC.dirty)
261*d5c9a868SElliott Hughes add_to_request(This, ptr,
262*d5c9a868SElliott Hughes requests, &nr,
263*d5c9a868SElliott Hughes MT_WRITE, &compactify);
264*d5c9a868SElliott Hughes #if 1
265*d5c9a868SElliott Hughes bytes = send_cmd(This->fd,requests, nr, "writing", 4);
266*d5c9a868SElliott Hughes if(bytes < 0)
267*d5c9a868SElliott Hughes return bytes;
268*d5c9a868SElliott Hughes #else
269*d5c9a868SElliott Hughes bytes = 0xffffff;
270*d5c9a868SElliott Hughes #endif
271*d5c9a868SElliott Hughes for(ptr=0; ptr < This->last_sector; ptr=REC.end)
272*d5c9a868SElliott Hughes if(REC.dirty) {
273*d5c9a868SElliott Hughes if(bytes >= REC.end - REC.begin) {
274*d5c9a868SElliott Hughes bytes -= REC.end - REC.begin;
275*d5c9a868SElliott Hughes REC.dirty = 0;
276*d5c9a868SElliott Hughes } else
277*d5c9a868SElliott Hughes return 1;
278*d5c9a868SElliott Hughes }
279*d5c9a868SElliott Hughes return 0;
280*d5c9a868SElliott Hughes }
281*d5c9a868SElliott Hughes
282*d5c9a868SElliott Hughes
283*d5c9a868SElliott Hughes
flush_dirty(Xdf_t * This)284*d5c9a868SElliott Hughes static int flush_dirty(Xdf_t *This)
285*d5c9a868SElliott Hughes {
286*d5c9a868SElliott Hughes int ret;
287*d5c9a868SElliott Hughes
288*d5c9a868SElliott Hughes while((ret = try_flush_dirty(This))) {
289*d5c9a868SElliott Hughes if(ret < 0)
290*d5c9a868SElliott Hughes return ret;
291*d5c9a868SElliott Hughes }
292*d5c9a868SElliott Hughes return 0;
293*d5c9a868SElliott Hughes }
294*d5c9a868SElliott Hughes
295*d5c9a868SElliott Hughes
load_data(Xdf_t * This,uint32_t ibegin,uint32_t iend,int retries)296*d5c9a868SElliott Hughes static ssize_t load_data(Xdf_t *This, uint32_t ibegin, uint32_t iend,
297*d5c9a868SElliott Hughes int retries)
298*d5c9a868SElliott Hughes {
299*d5c9a868SElliott Hughes unsigned char ptr;
300*d5c9a868SElliott Hughes int nr, bytes;
301*d5c9a868SElliott Hughes RawRequest_t requests[100];
302*d5c9a868SElliott Hughes Compactify_t compactify;
303*d5c9a868SElliott Hughes unsigned char begin, end;
304*d5c9a868SElliott Hughes adjust_bounds(This, ibegin, iend, &begin, &end);
305*d5c9a868SElliott Hughes
306*d5c9a868SElliott Hughes ptr = begin;
307*d5c9a868SElliott Hughes nr = 0;
308*d5c9a868SElliott Hughes for(ptr=REC.begin; ptr < end ; ptr = REC.end)
309*d5c9a868SElliott Hughes add_to_request_if_invalid(This, ptr, requests, &nr,
310*d5c9a868SElliott Hughes &compactify);
311*d5c9a868SElliott Hughes bytes = send_cmd(This->fd,requests, nr, "reading", retries);
312*d5c9a868SElliott Hughes if(bytes < 0)
313*d5c9a868SElliott Hughes return bytes;
314*d5c9a868SElliott Hughes ptr = begin;
315*d5c9a868SElliott Hughes for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
316*d5c9a868SElliott Hughes if(!REC.valid) {
317*d5c9a868SElliott Hughes if(bytes >= REC.end - REC.begin) {
318*d5c9a868SElliott Hughes bytes -= REC.end - REC.begin;
319*d5c9a868SElliott Hughes REC.valid = 1;
320*d5c9a868SElliott Hughes } else if(ptr > begin)
321*d5c9a868SElliott Hughes return ptr * This->sector_size;
322*d5c9a868SElliott Hughes else
323*d5c9a868SElliott Hughes return -1;
324*d5c9a868SElliott Hughes }
325*d5c9a868SElliott Hughes }
326*d5c9a868SElliott Hughes return end * This->sector_size;
327*d5c9a868SElliott Hughes }
328*d5c9a868SElliott Hughes
mark_dirty(Xdf_t * This,uint32_t ibegin,uint32_t iend)329*d5c9a868SElliott Hughes static void mark_dirty(Xdf_t *This, uint32_t ibegin, uint32_t iend)
330*d5c9a868SElliott Hughes {
331*d5c9a868SElliott Hughes int ptr;
332*d5c9a868SElliott Hughes unsigned char begin, end;
333*d5c9a868SElliott Hughes
334*d5c9a868SElliott Hughes adjust_bounds(This, ibegin, iend, &begin, &end);
335*d5c9a868SElliott Hughes
336*d5c9a868SElliott Hughes ptr = begin;
337*d5c9a868SElliott Hughes for(ptr=REC.begin; ptr < end ; ptr = REC.end) {
338*d5c9a868SElliott Hughes REC.valid = 1;
339*d5c9a868SElliott Hughes if(!REC.phantom)
340*d5c9a868SElliott Hughes REC.dirty = 1;
341*d5c9a868SElliott Hughes }
342*d5c9a868SElliott Hughes }
343*d5c9a868SElliott Hughes
344*d5c9a868SElliott Hughes
load_bounds(Xdf_t * This,uint32_t begin,uint32_t end)345*d5c9a868SElliott Hughes static ssize_t load_bounds(Xdf_t *This, uint32_t begin, uint32_t end)
346*d5c9a868SElliott Hughes {
347*d5c9a868SElliott Hughes unsigned char lbegin, lend;
348*d5c9a868SElliott Hughes
349*d5c9a868SElliott Hughes adjust_bounds(This, begin, end, &lbegin, &lend);
350*d5c9a868SElliott Hughes
351*d5c9a868SElliott Hughes if(begin != BEGIN(lbegin) * This->sector_size &&
352*d5c9a868SElliott Hughes end != BEGIN(lend) * This->sector_size &&
353*d5c9a868SElliott Hughes lend < END(END(lbegin)))
354*d5c9a868SElliott Hughes /* contiguous end & begin, load them in one go */
355*d5c9a868SElliott Hughes return load_data(This, begin, end, 4);
356*d5c9a868SElliott Hughes
357*d5c9a868SElliott Hughes if(begin != BEGIN(lbegin) * This->sector_size) {
358*d5c9a868SElliott Hughes ssize_t ret = load_data(This, begin, begin, 4);
359*d5c9a868SElliott Hughes if(ret < 0)
360*d5c9a868SElliott Hughes return ret;
361*d5c9a868SElliott Hughes }
362*d5c9a868SElliott Hughes
363*d5c9a868SElliott Hughes if(end != BEGIN(lend) * This->sector_size) {
364*d5c9a868SElliott Hughes ssize_t ret = load_data(This, end, end, 4);
365*d5c9a868SElliott Hughes if(ret < 0)
366*d5c9a868SElliott Hughes return BEGIN(lend) * This->sector_size;
367*d5c9a868SElliott Hughes }
368*d5c9a868SElliott Hughes return lend * This->sector_size;
369*d5c9a868SElliott Hughes }
370*d5c9a868SElliott Hughes
371*d5c9a868SElliott Hughes /* Fill out a map that is just sufficient to read boot sector */
fill_boot(Xdf_t * This)372*d5c9a868SElliott Hughes static void fill_boot(Xdf_t *This)
373*d5c9a868SElliott Hughes {
374*d5c9a868SElliott Hughes uint8_t ptr=0;
375*d5c9a868SElliott Hughes
376*d5c9a868SElliott Hughes REC.head = 0;
377*d5c9a868SElliott Hughes REC.sector = 129;
378*d5c9a868SElliott Hughes REC.phantom = 0;
379*d5c9a868SElliott Hughes
380*d5c9a868SElliott Hughes REC.begin = ptr;
381*d5c9a868SElliott Hughes REC.end = ptr+1;
382*d5c9a868SElliott Hughes
383*d5c9a868SElliott Hughes REC.sizecode = 2;
384*d5c9a868SElliott Hughes
385*d5c9a868SElliott Hughes REC.valid = 0;
386*d5c9a868SElliott Hughes REC.dirty = 0;
387*d5c9a868SElliott Hughes This->last_sector=1;
388*d5c9a868SElliott Hughes This->current_track=0;
389*d5c9a868SElliott Hughes }
390*d5c9a868SElliott Hughes
391*d5c9a868SElliott Hughes
fill_t0(Xdf_t * This,uint8_t ptr,unsigned int size,uint8_t * sector,uint8_t * head)392*d5c9a868SElliott Hughes static uint8_t fill_t0(Xdf_t *This, uint8_t ptr, unsigned int size,
393*d5c9a868SElliott Hughes uint8_t *sector, uint8_t *head)
394*d5c9a868SElliott Hughes {
395*d5c9a868SElliott Hughes unsigned int n;
396*d5c9a868SElliott Hughes
397*d5c9a868SElliott Hughes for(n = 0; n < size; ptr++,n++) {
398*d5c9a868SElliott Hughes REC.head = *head;
399*d5c9a868SElliott Hughes REC.sector = *sector + 129;
400*d5c9a868SElliott Hughes REC.phantom = 0;
401*d5c9a868SElliott Hughes (*sector)++;
402*d5c9a868SElliott Hughes if(!*head && *sector >= This->track0_size - 8) {
403*d5c9a868SElliott Hughes *sector = 0;
404*d5c9a868SElliott Hughes *head = 1;
405*d5c9a868SElliott Hughes }
406*d5c9a868SElliott Hughes }
407*d5c9a868SElliott Hughes return ptr;
408*d5c9a868SElliott Hughes }
409*d5c9a868SElliott Hughes
410*d5c9a868SElliott Hughes
fill_phantoms(Xdf_t * This,uint8_t ptr,uint8_t size)411*d5c9a868SElliott Hughes static uint8_t fill_phantoms(Xdf_t *This, uint8_t ptr, uint8_t size)
412*d5c9a868SElliott Hughes {
413*d5c9a868SElliott Hughes unsigned int n;
414*d5c9a868SElliott Hughes
415*d5c9a868SElliott Hughes for(n = 0; n < size; ptr++,n++)
416*d5c9a868SElliott Hughes REC.phantom = 1;
417*d5c9a868SElliott Hughes return ptr;
418*d5c9a868SElliott Hughes }
419*d5c9a868SElliott Hughes
decompose(Xdf_t * This,mt_off_t iwhere,size_t len,uint32_t * begin,uint32_t * end,uint8_t boot)420*d5c9a868SElliott Hughes static int decompose(Xdf_t *This, mt_off_t iwhere, size_t len,
421*d5c9a868SElliott Hughes uint32_t *begin, uint32_t *end, uint8_t boot)
422*d5c9a868SElliott Hughes {
423*d5c9a868SElliott Hughes uint8_t ptr;
424*d5c9a868SElliott Hughes sector_map_t *map;
425*d5c9a868SElliott Hughes uint8_t lbegin, lend;
426*d5c9a868SElliott Hughes uint32_t track_size = This->track_size * 1024;
427*d5c9a868SElliott Hughes
428*d5c9a868SElliott Hughes smt_off_t track = (smt_off_t) iwhere / track_size;
429*d5c9a868SElliott Hughes uint32_t where = (smt_off_t) iwhere % track_size;
430*d5c9a868SElliott Hughes
431*d5c9a868SElliott Hughes *begin = where;
432*d5c9a868SElliott Hughes if(where + len > track_size)
433*d5c9a868SElliott Hughes *end = track_size;
434*d5c9a868SElliott Hughes else
435*d5c9a868SElliott Hughes *end = (uint32_t) (where + len);
436*d5c9a868SElliott Hughes
437*d5c9a868SElliott Hughes if(This->current_track == track && !boot)
438*d5c9a868SElliott Hughes /* already OK, return immediately */
439*d5c9a868SElliott Hughes return 0;
440*d5c9a868SElliott Hughes if(!boot)
441*d5c9a868SElliott Hughes flush_dirty(This);
442*d5c9a868SElliott Hughes if(track >= 80)
443*d5c9a868SElliott Hughes return -1;
444*d5c9a868SElliott Hughes This->current_track = (uint8_t) track;
445*d5c9a868SElliott Hughes This->track_valid = true;
446*d5c9a868SElliott Hughes
447*d5c9a868SElliott Hughes if(track) {
448*d5c9a868SElliott Hughes for(ptr=0, map=This->map; map->size; map++) {
449*d5c9a868SElliott Hughes /* iterate through all sectors */
450*d5c9a868SElliott Hughes lbegin = ptr;
451*d5c9a868SElliott Hughes lend = ptr +
452*d5c9a868SElliott Hughes (uint8_t) ((128u<<map->size)/This->sector_size);
453*d5c9a868SElliott Hughes for( ; ptr < lend ; ptr++) {
454*d5c9a868SElliott Hughes REC.begin = lbegin;
455*d5c9a868SElliott Hughes REC.end = lend;
456*d5c9a868SElliott Hughes
457*d5c9a868SElliott Hughes REC.head = map->head;
458*d5c9a868SElliott Hughes REC.sector = map->size + 128;
459*d5c9a868SElliott Hughes REC.sizecode = map->size;
460*d5c9a868SElliott Hughes
461*d5c9a868SElliott Hughes REC.valid = 0;
462*d5c9a868SElliott Hughes REC.dirty = 0;
463*d5c9a868SElliott Hughes REC.phantom = 0;
464*d5c9a868SElliott Hughes }
465*d5c9a868SElliott Hughes }
466*d5c9a868SElliott Hughes REC.begin = REC.end = ptr;
467*d5c9a868SElliott Hughes } else {
468*d5c9a868SElliott Hughes uint8_t sector, head;
469*d5c9a868SElliott Hughes
470*d5c9a868SElliott Hughes head = 0;
471*d5c9a868SElliott Hughes sector = 0;
472*d5c9a868SElliott Hughes
473*d5c9a868SElliott Hughes for(ptr=boot; ptr < 2 * This->track_size; ptr++) {
474*d5c9a868SElliott Hughes REC.begin = ptr;
475*d5c9a868SElliott Hughes REC.end = ptr+1;
476*d5c9a868SElliott Hughes
477*d5c9a868SElliott Hughes REC.sizecode = 2;
478*d5c9a868SElliott Hughes
479*d5c9a868SElliott Hughes REC.valid = 0;
480*d5c9a868SElliott Hughes REC.dirty = 0;
481*d5c9a868SElliott Hughes }
482*d5c9a868SElliott Hughes
483*d5c9a868SElliott Hughes /* boot & 1st fat */
484*d5c9a868SElliott Hughes ptr=fill_t0(This, 0, 1 + This->FatSize, §or, &head);
485*d5c9a868SElliott Hughes
486*d5c9a868SElliott Hughes /* second fat */
487*d5c9a868SElliott Hughes ptr=fill_phantoms(This, ptr, This->FatSize);
488*d5c9a868SElliott Hughes
489*d5c9a868SElliott Hughes /* root dir */
490*d5c9a868SElliott Hughes ptr=fill_t0(This, ptr, This->RootDirSize, §or, &head);
491*d5c9a868SElliott Hughes
492*d5c9a868SElliott Hughes /* "bad sectors" at the beginning of the fs */
493*d5c9a868SElliott Hughes ptr=fill_phantoms(This, ptr, 5);
494*d5c9a868SElliott Hughes
495*d5c9a868SElliott Hughes if(This->rootskip)
496*d5c9a868SElliott Hughes sector++;
497*d5c9a868SElliott Hughes
498*d5c9a868SElliott Hughes /* beginning of the file system */
499*d5c9a868SElliott Hughes ptr = fill_t0(This, ptr,
500*d5c9a868SElliott Hughes (This->track_size - This->FatSize) * 2 -
501*d5c9a868SElliott Hughes This->RootDirSize - 6,
502*d5c9a868SElliott Hughes §or, &head);
503*d5c9a868SElliott Hughes }
504*d5c9a868SElliott Hughes This->last_sector = ptr;
505*d5c9a868SElliott Hughes return 0;
506*d5c9a868SElliott Hughes }
507*d5c9a868SElliott Hughes
508*d5c9a868SElliott Hughes
xdf_pread(Stream_t * Stream,char * buf,mt_off_t where,size_t len)509*d5c9a868SElliott Hughes static ssize_t xdf_pread(Stream_t *Stream, char *buf,
510*d5c9a868SElliott Hughes mt_off_t where, size_t len)
511*d5c9a868SElliott Hughes {
512*d5c9a868SElliott Hughes uint32_t begin, end;
513*d5c9a868SElliott Hughes ssize_t ret;
514*d5c9a868SElliott Hughes DeclareThis(Xdf_t);
515*d5c9a868SElliott Hughes
516*d5c9a868SElliott Hughes if(decompose(This, truncBytes32(where), len, &begin, &end, 0) < 0)
517*d5c9a868SElliott Hughes /* Read beyond end of device */
518*d5c9a868SElliott Hughes return 0;
519*d5c9a868SElliott Hughes ret = load_data(This, begin, end, 4);
520*d5c9a868SElliott Hughes if(ret < 0 || (size_t) ret < begin)
521*d5c9a868SElliott Hughes return -1;
522*d5c9a868SElliott Hughes maximize(len, (size_t) ret - begin);
523*d5c9a868SElliott Hughes memcpy(buf, This->buffer + begin, len);
524*d5c9a868SElliott Hughes return (ssize_t) (end - begin);
525*d5c9a868SElliott Hughes }
526*d5c9a868SElliott Hughes
xdf_pwrite(Stream_t * Stream,char * buf,mt_off_t where,size_t len)527*d5c9a868SElliott Hughes static ssize_t xdf_pwrite(Stream_t *Stream, char *buf,
528*d5c9a868SElliott Hughes mt_off_t where, size_t len)
529*d5c9a868SElliott Hughes {
530*d5c9a868SElliott Hughes uint32_t begin, end;
531*d5c9a868SElliott Hughes ssize_t len2;
532*d5c9a868SElliott Hughes DeclareThis(Xdf_t);
533*d5c9a868SElliott Hughes
534*d5c9a868SElliott Hughes if(decompose(This, truncBytes32(where), len, &begin, &end, 0) < 0) {
535*d5c9a868SElliott Hughes /* Write beyond end of device */
536*d5c9a868SElliott Hughes errno = EFBIG;
537*d5c9a868SElliott Hughes return -1;
538*d5c9a868SElliott Hughes }
539*d5c9a868SElliott Hughes
540*d5c9a868SElliott Hughes len2 = load_bounds(This, begin, end);
541*d5c9a868SElliott Hughes if(len2 < 0)
542*d5c9a868SElliott Hughes return -1;
543*d5c9a868SElliott Hughes maximize(end, (uint32_t)len2);
544*d5c9a868SElliott Hughes len2 -= begin;
545*d5c9a868SElliott Hughes maximize(len, (size_t) len2);
546*d5c9a868SElliott Hughes memcpy(This->buffer + begin, buf, len);
547*d5c9a868SElliott Hughes mark_dirty(This, begin, end);
548*d5c9a868SElliott Hughes return (ssize_t) (end - begin);
549*d5c9a868SElliott Hughes }
550*d5c9a868SElliott Hughes
xdf_flush(Stream_t * Stream)551*d5c9a868SElliott Hughes static int xdf_flush(Stream_t *Stream)
552*d5c9a868SElliott Hughes {
553*d5c9a868SElliott Hughes DeclareThis(Xdf_t);
554*d5c9a868SElliott Hughes
555*d5c9a868SElliott Hughes return flush_dirty(This);
556*d5c9a868SElliott Hughes }
557*d5c9a868SElliott Hughes
xdf_free(Stream_t * Stream)558*d5c9a868SElliott Hughes static int xdf_free(Stream_t *Stream)
559*d5c9a868SElliott Hughes {
560*d5c9a868SElliott Hughes DeclareThis(Xdf_t);
561*d5c9a868SElliott Hughes Free(This->track_map);
562*d5c9a868SElliott Hughes Free(This->buffer);
563*d5c9a868SElliott Hughes return close(This->fd);
564*d5c9a868SElliott Hughes }
565*d5c9a868SElliott Hughes
566*d5c9a868SElliott Hughes
check_geom(Xdf_t * This,struct device * dev)567*d5c9a868SElliott Hughes static int check_geom(Xdf_t *This, struct device *dev)
568*d5c9a868SElliott Hughes {
569*d5c9a868SElliott Hughes unsigned int sect;
570*d5c9a868SElliott Hughes
571*d5c9a868SElliott Hughes if (!IS_MFORMAT_ONLY(dev)) {
572*d5c9a868SElliott Hughes if(compare(dev->sectors, 19) &&
573*d5c9a868SElliott Hughes compare(dev->sectors, 23) &&
574*d5c9a868SElliott Hughes compare(dev->sectors, 24) &&
575*d5c9a868SElliott Hughes compare(dev->sectors, 46) &&
576*d5c9a868SElliott Hughes compare(dev->sectors, 48))
577*d5c9a868SElliott Hughes return 1;
578*d5c9a868SElliott Hughes
579*d5c9a868SElliott Hughes /* check against contradictory info from configuration file */
580*d5c9a868SElliott Hughes if(compare(dev->heads, 2))
581*d5c9a868SElliott Hughes return 1;
582*d5c9a868SElliott Hughes }
583*d5c9a868SElliott Hughes
584*d5c9a868SElliott Hughes /* check against info from boot */
585*d5c9a868SElliott Hughes if(This) {
586*d5c9a868SElliott Hughes sect = This->track_size;
587*d5c9a868SElliott Hughes if((sect != 19 && sect != 23 && sect != 24 &&
588*d5c9a868SElliott Hughes sect != 46 && sect != 48) ||
589*d5c9a868SElliott Hughes (!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)))
590*d5c9a868SElliott Hughes return 1;
591*d5c9a868SElliott Hughes }
592*d5c9a868SElliott Hughes return 0;
593*d5c9a868SElliott Hughes }
594*d5c9a868SElliott Hughes
set_geom(Xdf_t * This,struct device * dev)595*d5c9a868SElliott Hughes static void set_geom(Xdf_t *This, struct device *dev)
596*d5c9a868SElliott Hughes {
597*d5c9a868SElliott Hughes /* fill in config info to be returned to user */
598*d5c9a868SElliott Hughes dev->heads = 2;
599*d5c9a868SElliott Hughes dev->use_2m = 0xff;
600*d5c9a868SElliott Hughes dev->sectors = (uint16_t) This->track_size;
601*d5c9a868SElliott Hughes dev->tracks = 80;
602*d5c9a868SElliott Hughes }
603*d5c9a868SElliott Hughes
config_geom(Stream_t * Stream UNUSEDP,struct device * dev,struct device * orig_dev UNUSEDP)604*d5c9a868SElliott Hughes static int config_geom(Stream_t *Stream UNUSEDP, struct device *dev,
605*d5c9a868SElliott Hughes struct device *orig_dev UNUSEDP)
606*d5c9a868SElliott Hughes {
607*d5c9a868SElliott Hughes DeclareThis(Xdf_t);
608*d5c9a868SElliott Hughes if(check_geom(This, dev))
609*d5c9a868SElliott Hughes return 1;
610*d5c9a868SElliott Hughes set_geom(This, dev);
611*d5c9a868SElliott Hughes return 0;
612*d5c9a868SElliott Hughes }
613*d5c9a868SElliott Hughes
614*d5c9a868SElliott Hughes static Class_t XdfClass = {
615*d5c9a868SElliott Hughes 0,
616*d5c9a868SElliott Hughes 0,
617*d5c9a868SElliott Hughes xdf_pread,
618*d5c9a868SElliott Hughes xdf_pwrite,
619*d5c9a868SElliott Hughes xdf_flush,
620*d5c9a868SElliott Hughes xdf_free,
621*d5c9a868SElliott Hughes config_geom,
622*d5c9a868SElliott Hughes 0, /* get_data */
623*d5c9a868SElliott Hughes 0, /* pre-allocate */
624*d5c9a868SElliott Hughes 0, /* get_dosConvert */
625*d5c9a868SElliott Hughes 0 /* discard */
626*d5c9a868SElliott Hughes };
627*d5c9a868SElliott Hughes
XdfOpen(struct device * dev,const char * name,int mode,char * errmsg,struct xdf_info * info)628*d5c9a868SElliott Hughes Stream_t *XdfOpen(struct device *dev, const char *name,
629*d5c9a868SElliott Hughes int mode, char *errmsg, struct xdf_info *info)
630*d5c9a868SElliott Hughes {
631*d5c9a868SElliott Hughes Xdf_t *This;
632*d5c9a868SElliott Hughes uint32_t begin, end;
633*d5c9a868SElliott Hughes union bootsector *boot;
634*d5c9a868SElliott Hughes unsigned int type;
635*d5c9a868SElliott Hughes uint16_t fatSize;
636*d5c9a868SElliott Hughes
637*d5c9a868SElliott Hughes if(dev && ((!SHOULD_USE_XDF(dev) && !getenv("MTOOLS_USE_XDF")) ||
638*d5c9a868SElliott Hughes check_geom(NULL, dev)))
639*d5c9a868SElliott Hughes return NULL;
640*d5c9a868SElliott Hughes
641*d5c9a868SElliott Hughes This = New(Xdf_t);
642*d5c9a868SElliott Hughes if (!This)
643*d5c9a868SElliott Hughes return NULL;
644*d5c9a868SElliott Hughes init_head(&This->head, &XdfClass, NULL);
645*d5c9a868SElliott Hughes
646*d5c9a868SElliott Hughes This->sector_size = 512;
647*d5c9a868SElliott Hughes This->stretch = 0;
648*d5c9a868SElliott Hughes
649*d5c9a868SElliott Hughes precmd(dev);
650*d5c9a868SElliott Hughes This->fd = open(name,
651*d5c9a868SElliott Hughes ((mode | dev->mode) & ~O_ACCMODE) |
652*d5c9a868SElliott Hughes O_EXCL | O_NDELAY | O_RDWR);
653*d5c9a868SElliott Hughes if(This->fd < 0) {
654*d5c9a868SElliott Hughes #ifdef HAVE_SNPRINTF
655*d5c9a868SElliott Hughes snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno));
656*d5c9a868SElliott Hughes #else
657*d5c9a868SElliott Hughes sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno));
658*d5c9a868SElliott Hughes #endif
659*d5c9a868SElliott Hughes goto exit_0;
660*d5c9a868SElliott Hughes }
661*d5c9a868SElliott Hughes closeExec(This->fd);
662*d5c9a868SElliott Hughes
663*d5c9a868SElliott Hughes This->drive = GET_DRIVE(This->fd);
664*d5c9a868SElliott Hughes if(This->drive < 0)
665*d5c9a868SElliott Hughes goto exit_1;
666*d5c9a868SElliott Hughes
667*d5c9a868SElliott Hughes /* allocate buffer */
668*d5c9a868SElliott Hughes This->buffer = (char *) malloc(96 * 512);
669*d5c9a868SElliott Hughes if (!This->buffer)
670*d5c9a868SElliott Hughes goto exit_1;
671*d5c9a868SElliott Hughes
672*d5c9a868SElliott Hughes This->track_valid = false;
673*d5c9a868SElliott Hughes This->track_map = (TrackMap_t *)
674*d5c9a868SElliott Hughes calloc(96, sizeof(TrackMap_t));
675*d5c9a868SElliott Hughes if(!This->track_map)
676*d5c9a868SElliott Hughes goto exit_2;
677*d5c9a868SElliott Hughes
678*d5c9a868SElliott Hughes /* lock the device on writes */
679*d5c9a868SElliott Hughes if (lock_dev(This->fd, mode == O_RDWR, dev)) {
680*d5c9a868SElliott Hughes #ifdef HAVE_SNPRINTF
681*d5c9a868SElliott Hughes snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:",
682*d5c9a868SElliott Hughes dev->name);
683*d5c9a868SElliott Hughes #else
684*d5c9a868SElliott Hughes sprintf(errmsg,"xdf floppy: device \"%s\" busy:",
685*d5c9a868SElliott Hughes dev->name);
686*d5c9a868SElliott Hughes #endif
687*d5c9a868SElliott Hughes goto exit_3;
688*d5c9a868SElliott Hughes }
689*d5c9a868SElliott Hughes
690*d5c9a868SElliott Hughes /* Before reading the boot sector, assume dummy values suitable
691*d5c9a868SElliott Hughes * for reading just the boot sector */
692*d5c9a868SElliott Hughes fill_boot(This);
693*d5c9a868SElliott Hughes This->rate = 0;
694*d5c9a868SElliott Hughes if (load_data(This, 0, 1, 4) < 0 ) {
695*d5c9a868SElliott Hughes This->rate = 0x43;
696*d5c9a868SElliott Hughes if(load_data(This, 0, 1, 4) < 0)
697*d5c9a868SElliott Hughes goto exit_3;
698*d5c9a868SElliott Hughes }
699*d5c9a868SElliott Hughes
700*d5c9a868SElliott Hughes boot = (union bootsector *) This->buffer;
701*d5c9a868SElliott Hughes
702*d5c9a868SElliott Hughes fatSize = WORD(fatlen);
703*d5c9a868SElliott Hughes if(fatSize > UINT8_MAX) {
704*d5c9a868SElliott Hughes fprintf(stderr, "Fat size %d too large\n", fatSize);
705*d5c9a868SElliott Hughes exit(1);
706*d5c9a868SElliott Hughes }
707*d5c9a868SElliott Hughes This->FatSize = (uint8_t) fatSize;
708*d5c9a868SElliott Hughes This->RootDirSize = WORD(dirents)/16;
709*d5c9a868SElliott Hughes This->track_size = WORD(nsect);
710*d5c9a868SElliott Hughes for(type=0; type < NUMBER(xdf_table); type++) {
711*d5c9a868SElliott Hughes if(xdf_table[type].track_size == This->track_size) {
712*d5c9a868SElliott Hughes This->map = xdf_table[type].map;
713*d5c9a868SElliott Hughes This->track0_size = xdf_table[type].track0_size;
714*d5c9a868SElliott Hughes This->rootskip = xdf_table[type].rootskip;
715*d5c9a868SElliott Hughes This->rate = xdf_table[type].rate;
716*d5c9a868SElliott Hughes break;
717*d5c9a868SElliott Hughes }
718*d5c9a868SElliott Hughes }
719*d5c9a868SElliott Hughes if(type == NUMBER(xdf_table))
720*d5c9a868SElliott Hughes goto exit_3;
721*d5c9a868SElliott Hughes
722*d5c9a868SElliott Hughes if(info) {
723*d5c9a868SElliott Hughes info->RootDirSize = This->RootDirSize;
724*d5c9a868SElliott Hughes info->FatSize = This->FatSize;
725*d5c9a868SElliott Hughes info->BadSectors = 5;
726*d5c9a868SElliott Hughes }
727*d5c9a868SElliott Hughes decompose(This, 0, 512, &begin, &end, 1);
728*d5c9a868SElliott Hughes
729*d5c9a868SElliott Hughes if(dev)
730*d5c9a868SElliott Hughes set_geom(This, dev);
731*d5c9a868SElliott Hughes return &This->head;
732*d5c9a868SElliott Hughes
733*d5c9a868SElliott Hughes exit_3:
734*d5c9a868SElliott Hughes Free(This->track_map);
735*d5c9a868SElliott Hughes exit_2:
736*d5c9a868SElliott Hughes Free(This->buffer);
737*d5c9a868SElliott Hughes exit_1:
738*d5c9a868SElliott Hughes close(This->fd);
739*d5c9a868SElliott Hughes exit_0:
740*d5c9a868SElliott Hughes Free(This);
741*d5c9a868SElliott Hughes return NULL;
742*d5c9a868SElliott Hughes }
743*d5c9a868SElliott Hughes
744*d5c9a868SElliott Hughes #endif
745*d5c9a868SElliott Hughes
746*d5c9a868SElliott Hughes /* Algorithms can't be patented */
747