]> rtime.felk.cvut.cz Git - mf6xx.git/blob - src/hudaqlib/PCD7004.c
QEMU mf624.c formatted to make QEMU checkpatch.pl mostly happy.
[mf6xx.git] / src / hudaqlib / PCD7004.c
1 /****************************************************************/\r
2 /**@file PCD7004.c:\r
3  * Description: API layer for TEDIA PCD-7004 card.              *\r
4  * Dependency: Windows 32, Windows 64 or Linux                  *\r
5  *                Copyright 2009-2010 Jaroslav Fojtik           *\r
6  ****************************************************************/\r
7 \r
8 #if defined(_WIN32) || defined(_WIN64)\r
9 #include <windows.h>\r
10 #else\r
11 #include <sys/io.h>\r
12 #endif\r
13 #include <malloc.h>\r
14 #include <math.h>\r
15 \r
16 #include "hudaqlib.h"\r
17 #include "hudaq_internal.h"\r
18 \r
19 \r
20 #define DI_CHANNELS   4         ///< Amount of Digital Outputs\r
21 #define DO_CHANNELS   4         ///< Amount of Digital Inputs\r
22 #define DA_CHANNELS   0         ///< Amount of Analog Outputs\r
23 #define AD_CHANNELS   0         ///< Amount of Analog Inputs\r
24 #define ENC_CHANNELS  0         ///< Amount of Encoders\r
25 #define CTR_CHANNELS  1         ///< Amount of counters\r
26 #define STEP_CHANNELS 0         ///< Amount of steppers\r
27 \r
28 \r
29 #define MASTERFREQUENCY 1000        ///< Internal oscillator frequency is 1kHz\r
30 \r
31 \r
32 /** Cache of PCD-7004 state */\r
33 typedef struct\r
34 {\r
35         UserDataHeader Hdr;                   ///< General device setup\r
36 \r
37         unsigned __int8 DIOReg[4];              ///< Port configuration register\r
38         unsigned __int8 DIOCfgReg;\r
39         unsigned __int8 IRQCfgReg;\r
40         unsigned __int8 INTEnReg;\r
41         unsigned __int8 TimerReg;\r
42 } PCD_7004_Private;\r
43 \r
44 \r
45 /** Symbolic aliases for available registers inside BADR4 read. */\r
46 typedef enum\r
47 {\r
48         DIOReg0    = 0,\r
49         DIOReg1    = 1, //4\r
50         DIOReg2    = 2, //8,\r
51         DIOReg3    = 3, //0xC,\r
52 \r
53         DIOCfgReg  = 0x80/4,\r
54 \r
55         IRQCfgReg  = 0x200/4,           // WR only\r
56         IRQStatusReg = 0x200/4, // RD only\r
57         IRQClrReg =  0x204/4,           // WR only\r
58 \r
59         TimerReg =  0x208/4,            // RW\r
60         INTEnReg =  0x20C/4,\r
61 \r
62 } MEM4IO;\r
63 \r
64 static __int8 Ports[DI_CHANNELS] = {DIOReg0, DIOReg1, DIOReg2, DIOReg3};\r
65 \r
66 \r
67 /** write to memory mapped device byte wise, bytes are stored on every 4th position. */\r
68 static __inline void StoreByte(size_t Ptr, int Offset, unsigned __int8 value)\r
69 {\r
70         ((volatile unsigned __int8 *)(Ptr))[4*Offset] = value;\r
71 }\r
72 \r
73 /** write to memory mapped device byte wise through cache. */\r
74 static __inline void StoreCachedByte(size_t Ptr, int Offset, unsigned __int8 value, unsigned __int8 *CachedValue)\r
75 {\r
76         if (*CachedValue != value)\r
77         {\r
78                 *CachedValue = value;\r
79                 StoreByte(Ptr,Offset,value);\r
80         }\r
81 }\r
82 \r
83 \r
84 /** This inline is used for reading tightly packed OX9162 local configuration registers. */\r
85 static __inline unsigned __int8 GetBytePlain(size_t Ptr, int Offset)\r
86 {\r
87         return ((volatile unsigned __int8 *)(Ptr))[4*Offset];\r
88 }\r
89 \r
90 \r
91 /****************************************************************\r
92  *                                                              *\r
93  *                 DIGITAL INPUTS & OUTPUTS                     *\r
94  *                                                              *\r
95  ****************************************************************/\r
96 \r
97 \r
98 /** Get data from digital input. */\r
99 static int PCD7004DIRead(const DeviceRecord *DevRecord, unsigned channel)\r
100 {\r
101         if (channel>=DI_CHANNELS) return HUDAQBADARG;\r
102         return GetBytePlain(DevRecord->DrvRes.Resources.MemResources[1].Base,Ports[channel]);\r
103 }\r
104 \r
105 \r
106 /** Write data to digital output. */\r
107 static HUDAQSTATUS PCD7004DOWrite(const DeviceRecord *DevRecord, unsigned channel, unsigned value)\r
108 {\r
109         if (channel>=DO_CHANNELS) return HUDAQBADARG;\r
110         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, Ports[channel], value,\r
111                         &((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel] );\r
112         return HUDAQSUCCESS;\r
113 }\r
114 \r
115 \r
116 static HUDAQSTATUS PCD7004DIReadMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned *channels, unsigned *values)\r
117 {\r
118         unsigned FlagX = 0;\r
119         unsigned i;\r
120         size_t Base;\r
121         unsigned __int8 Cache[DI_CHANNELS+1];  //allocate more bytes for padding ind\r
122 \r
123         if (number==0 || channels==NULL || values==NULL) return HUDAQBADARG;\r
124         if(DevRecord->pCT->HudaqDIRead==NULL) return HUDAQFAILURE;\r
125 \r
126         Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
127 \r
128         /* Schedule what to read. */\r
129         for(i=0; i<number; i++)\r
130         {\r
131                 if (channels[i]<DI_CHANNELS)\r
132                 {\r
133                         FlagX |= 1<<(channels[i]);\r
134                 }\r
135                 else\r
136                 {\r
137                         values[i] = 0;\r
138                         FlagX |= 0x8000;\r
139                 }\r
140         }\r
141 \r
142         /* Read isolated DIs */\r
143         for(i=0; i<DI_CHANNELS; i++)\r
144         {\r
145                 if(FlagX & (1<<i))\r
146                 {\r
147                         Cache[i] = GetBytePlain(Base,Ports[i]);\r
148                 }\r
149         }  \r
150 \r
151         /* Final store of numbers read. */\r
152         for(i=0; i<number; i++)\r
153         {\r
154                 if(channels[i]<DI_CHANNELS)\r
155                 {\r
156                         values[i] = Cache[channels[i]];\r
157                 }\r
158         }      \r
159 \r
160         if(FlagX == 0x8000) return HUDAQFAILURE;        //no valid value returned\r
161         if(FlagX & 0x8000) return HUDAQPARTIAL; //several values are valid and several are not\r
162         return HUDAQSUCCESS;                    //all values are valid (or no channel asked)\r
163 }\r
164 \r
165 \r
166 static HUDAQSTATUS PCD7004DOWriteMultiple(const DeviceRecord *DevRecord, unsigned number, const unsigned *channels, const unsigned *values)\r
167 {\r
168         unsigned FlagX = 0;\r
169         unsigned i;\r
170         size_t Base;\r
171         unsigned __int8 *Cache;\r
172 \r
173         if (number==0 || channels==NULL || values==NULL) return HUDAQBADARG;\r
174         if(DevRecord->pCT->HudaqDIRead==NULL) return HUDAQFAILURE;\r
175 \r
176         Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
177         Cache = ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg;\r
178 \r
179         /* schedule what to write */\r
180         for(i=0; i<number; i++)\r
181         {\r
182                 if (channels[i]<DI_CHANNELS)\r
183                 {\r
184                         if(Cache[i] != values[i])\r
185                         {\r
186                                 FlagX |= 1<<(channels[i]);\r
187                                 Cache[i] = values[i];\r
188                         }\r
189                 }\r
190                 else\r
191                 {\r
192                         FlagX |= 0x8000;\r
193                 }\r
194         }\r
195 \r
196         /* Final store of DOs */\r
197         for(i=0; i<DI_CHANNELS; i++)\r
198         {\r
199                 if(FlagX & (1<<i))\r
200                 {\r
201                         StoreByte(Base, Ports[i], Cache[i]);\r
202                 }\r
203         }  \r
204 \r
205         if(FlagX == 0x8000) return HUDAQFAILURE;        //no valid value returned\r
206         if(FlagX & 0x8000) return HUDAQPARTIAL; //several values are valid and several are not\r
207         return HUDAQSUCCESS;                    //all values are valid (or no channel asked)\r
208 }\r
209 \r
210 \r
211 /** Write one bit to digital output. */\r
212 static void PCD7004DOWriteBit(const DeviceRecord *DevRecord, unsigned channel, unsigned bit, int value)\r
213 {\r
214         __int8 DoutByte;\r
215 \r
216         if (channel>=DO_CHANNELS) return;\r
217         if (bit>=8) return;             /* There are only 8 bits available in PCD7004. */\r
218 \r
219         DoutByte = (((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel]);\r
220         if (value) DoutByte |= (1<<bit);\r
221         else DoutByte &= ~(1<<bit);\r
222 \r
223         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, Ports[channel], DoutByte,\r
224                         & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel]);\r
225 }\r
226 \r
227 \r
228 static void PCD7004DOWriteMultipleBits(const DeviceRecord *DevRecord, unsigned channel, unsigned mask, unsigned value)\r
229 {\r
230         __int8 DoutByte;\r
231 \r
232         if (channel>=DO_CHANNELS) return;\r
233 \r
234         DoutByte = (((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel]);\r
235         DoutByte = (DoutByte & ~mask) | (value & mask);\r
236 \r
237         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, Ports[channel], DoutByte,\r
238                         & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOReg[channel] );\r
239 }\r
240 \r
241 \r
242 static double PCD7004DIOGetParameter(unsigned channel, HudaqParameter param)\r
243 {\r
244         switch((int)param)\r
245         {\r
246                 case HudaqDINUMBITS:\r
247                         return (channel<DI_CHANNELS) ? 8 : WRONG_VALUE;\r
248                 case HudaqDONUMBITS:\r
249                         return (channel<DO_CHANNELS) ? 8 : WRONG_VALUE;\r
250                 case HudaqDINUMCHANNELS:\r
251                         return DI_CHANNELS;\r
252                 case HudaqDONUMCHANNELS:\r
253                         return DO_CHANNELS;\r
254         }\r
255 \r
256         return WRONG_VALUE;\r
257 }\r
258 \r
259 \r
260 static HUDAQSTATUS PCD7004DIOSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, int value)\r
261 {\r
262         unsigned __int8 chval;\r
263 \r
264         switch((int)param)\r
265         {\r
266                 case HudaqDINUMBITS:\r
267                         if(channel>=DI_CHANNELS) return WRONG_VALUE;\r
268                         return (value==8) ? 0 : WRONG_VALUE;\r
269                 case HudaqDONUMBITS:\r
270                         if(channel>=DI_CHANNELS) return WRONG_VALUE;\r
271                         return (value==8) ? 0 : WRONG_VALUE;\r
272                 case HudaqDINUMCHANNELS:\r
273                         return DI_CHANNELS;\r
274                 case HudaqDONUMCHANNELS:\r
275                         return DO_CHANNELS;\r
276                 case HudaqDOMODE:\r
277                         if(channel>=DO_CHANNELS) return WRONG_VALUE;\r
278                         chval = ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOCfgReg;\r
279                         //printf("chval =  %d\n",chval);          \r
280 \r
281                         switch(channel)\r
282                         {\r
283                                 case 0: if(value==1) chval &= ~0x1;\r
284                                                 if(value==0) chval |= 0x1;\r
285                                         break;\r
286                                 case 1: if(value==1) chval &= ~0x02;\r
287                                                 if(value==0) chval |= 0x02;\r
288                                         break;\r
289                                 case 2: if(value==1) chval &= ~0x04;\r
290                                                 if(value==0) chval |= 0x04;\r
291                                         break;\r
292                                 case 3: if(value==1) chval &= ~0x08;\r
293                                                 if(value==0) chval |= 0x08;\r
294                                         break;\r
295                         }\r
296                         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, DIOCfgReg, chval,\r
297                                         & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->DIOCfgReg );\r
298                         return HUDAQSUCCESS;\r
299         }\r
300 \r
301         return WRONG_VALUE;\r
302 }\r
303 \r
304 \r
305 /****************************************************************\r
306  *                                                              *\r
307  *                COUNTERS PWM + Count + Step                   *\r
308  *                                                              *\r
309  ****************************************************************/\r
310 \r
311 static HUDAQSTATUS PCD7004PWMWrite(const DeviceRecord *DevRecord, unsigned channel, double frequency, double dutycycle)\r
312 {\r
313         double T;\r
314         HUDAQSTATUS ret = HUDAQSUCCESS;\r
315 \r
316         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
317 \r
318         if (frequency<=0) return HUDAQBADARG; //Allow duty cycle 0 and 1 for f=0.\r
319 \r
320         T = MASTERFREQUENCY/frequency;\r
321         if(T<1) \r
322         {\r
323                 StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, 1,\r
324                                 & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
325                 return HUDAQPARTIAL;\r
326         }\r
327         if(T>255)\r
328         {\r
329                 StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, 255,\r
330                                 & ((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
331                 return HUDAQPARTIAL;\r
332         }\r
333 \r
334         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, \r
335                         (unsigned __int8)T,\r
336                         &((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
337 \r
338         return HUDAQSUCCESS;\r
339 }\r
340 \r
341 \r
342 static HUDAQSTATUS PCD7004CtrReset(const DeviceRecord *DevRecord, unsigned channel)\r
343 {\r
344         if (channel>=CTR_CHANNELS) return HUDAQBADARG;\r
345         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg, \r
346                         0, &((PCD_7004_Private *)(DevRecord->DrvRes.DriverData))->TimerReg);\r
347         return HUDAQSUCCESS;\r
348 }\r
349 \r
350 \r
351 static int PCD7004CtrRead(const DeviceRecord *DevRecord, unsigned channel)\r
352 {\r
353         if (channel>=CTR_CHANNELS) return 0;\r
354         return GetBytePlain(DevRecord->DrvRes.Resources.MemResources[1].Base, TimerReg);\r
355 }\r
356 \r
357 \r
358 \r
359 \r
360 \r
361 /********************************************************************/\r
362 \r
363 /* Initialization procedure for PCD-7004 card. */\r
364 static int InitPCD7004(DeviceRecord *DevRecord, int IniOptions)\r
365 {\r
366         PCD_7004_Private *Cache;\r
367         int i;\r
368         size_t Base;\r
369 \r
370         if (DevRecord==NULL) return -1;\r
371 \r
372         if (DevRecord->DrvRes.Resources.NumMemResources<2) return -2;  //insufficient amount of resources\r
373         Cache = DevRecord->DrvRes.DriverData;\r
374         if (Cache==NULL) return -3;\r
375         if (DevRecord->DrvRes.DriverDataSize<sizeof(PCD_7004_Private)) return -4;\r
376 \r
377         DevRecord->pCT = &CTPCD7004;\r
378 \r
379         if ((IniOptions & HudaqOpenNOINIT)==0)\r
380         {                           //initialize hardware only if no other application is running\r
381                 Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
382 \r
383                 /* disable interrupts */\r
384                 Cache->INTEnReg = 0;\r
385                 StoreByte(Base, INTEnReg, 0);\r
386 \r
387                 /* Initialization of DI and DO. */\r
388                 for(i=0; i<4; i++)\r
389                 {\r
390                         Cache->DIOReg[i] = 0;\r
391                         StoreByte(Base, Ports[i], 0);\r
392                 }\r
393 \r
394                 Cache->DIOCfgReg = 0;\r
395                 StoreByte(Base, DIOCfgReg, 0);\r
396 \r
397                 Cache->IRQCfgReg = 0;\r
398                 StoreByte(Base, IRQCfgReg, 0);\r
399 \r
400                 Cache->TimerReg = 0;\r
401                 StoreByte(Base, TimerReg, 0);   /* Stop timer. */\r
402         }\r
403 \r
404         return 1; //success\r
405 }\r
406 \r
407 \r
408 /** Internal cleanup procedure for PCD7004 */\r
409 static void DonePCD7004(DeviceRecord *DevRecord)\r
410 {\r
411         if (DevRecord==NULL) return;\r
412 \r
413         DevRecord->pCT = &CtDummy;\r
414 }\r
415 \r
416 \r
417 \r
418 /****************************************************************\r
419  *                                                              *\r
420  *                    GET/SET PARAMETERS                        *\r
421  *                                                              *\r
422  ****************************************************************/\r
423 \r
424 \r
425 static HUDAQSTATUS PCD7004IRQSetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, int value)\r
426 {\r
427         size_t Base;\r
428 \r
429         Base = DevRecord->DrvRes.Resources.MemResources[1].Base;\r
430 \r
431         switch((int)param)\r
432         {\r
433                 case HudaqIRQ+0:\r
434                         {\r
435                                 if(value)\r
436                                 {\r
437                                         StoreCachedByte(Base, IRQCfgReg, 1, &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->IRQCfgReg);\r
438                                         StoreByte(Base, IRQClrReg, 0xFF);\r
439                                         StoreCachedByte(Base, INTEnReg, 0x80, &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->INTEnReg);\r
440                                 }\r
441                                 else\r
442                                 { \r
443                                         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, INTEnReg, 0,\r
444                                                         &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->INTEnReg);\r
445                                         StoreCachedByte(DevRecord->DrvRes.Resources.MemResources[1].Base, IRQCfgReg, 0,\r
446                                                         &((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->IRQCfgReg);\r
447                                 }\r
448                                 return HUDAQSUCCESS;\r
449                         }\r
450                 case HudaqIRQ+1:\r
451                         return HUDAQSUCCESS;\r
452         }\r
453 \r
454         return WRONG_VALUE;\r
455 }\r
456 \r
457 \r
458 static double PCD7004IRQGetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
459 {\r
460         switch((int)param)\r
461         {\r
462                 case HudaqIRQ+0: \r
463                         return ((PCD_7004_Private *)DevRecord->DrvRes.DriverData)->Hdr.IRQcounter;\r
464                 case HudaqIRQ+1:\r
465                         return GetBytePlain(DevRecord->DrvRes.Resources.MemResources[1].Base,IRQStatusReg);\r
466         }\r
467         return WRONG_VALUE;\r
468 }\r
469 \r
470 \r
471 static HUDAQSTATUS PCD7004SetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param, double value)\r
472 {\r
473         switch(param & HudaqSubsystemMASK)\r
474         {\r
475                 case HudaqDI:\r
476                 case HudaqDO:  return PCD7004DIOSetParameter(DevRecord,channel,param,(int)value);\r
477                                //  case HudaqAI:  \r
478                                //  case HudaqAO:  \r
479                                //  case HudaqEnc: \r
480                                //  case HudaqPWM:\r
481                                //  case HudaqCtr: \r
482                                //  case HudaqStep:\r
483                 case HudaqIRQ: return PCD7004IRQSetParameter(DevRecord,channel,param,(int)value);\r
484         }\r
485         return HUDAQNOTSUPPORTED;\r
486 }\r
487 \r
488 static double PCD7004GetParameter(const DeviceRecord *DevRecord, unsigned channel, HudaqParameter param)\r
489 {\r
490         switch(param & HudaqSubsystemMASK)\r
491         {\r
492                 case HudaqDI:\r
493                 case HudaqDO:  return PCD7004DIOGetParameter(channel,param);\r
494                                //case HudaqAI:  \r
495                                //case HudaqAO:  \r
496                                //case HudaqEnc:\r
497                                //case HudaqPWM:\r
498                                //case HudaqCtr:\r
499                                //case HudaqStep:\r
500                 case HudaqIRQ: return PCD7004IRQGetParameter(DevRecord,channel,param);\r
501         }\r
502         return WRONG_VALUE;\r
503 }\r
504 \r
505 \r
506 const CallTable CTPCD7004 =\r
507 {\r
508         "PCD7004", 0x1760, 0x0101,\r
509         InitPCD7004,\r
510         DonePCD7004,\r
511 \r
512         PCD7004SetParameter,\r
513         PCD7004GetParameter,\r
514         NULL,\r
515 \r
516         // INITIALIZE DI callers\r
517         PCD7004DIRead,\r
518         GenericDIReadBit,           //Generic implementation\r
519         PCD7004DIReadMultiple,\r
520         // INITIALIZE DO callers\r
521         PCD7004DOWrite,\r
522         PCD7004DOWriteBit,\r
523         PCD7004DOWriteMultipleBits,\r
524         PCD7004DOWriteMultiple,\r
525         // INITIALIZE AI callers\r
526         NULL,\r
527         NULL,\r
528         // INITIALIZE AO callers\r
529         NULL,\r
530         NULL,\r
531         // INITIALIZE Enc callers\r
532         NULL,                       // Not available\r
533         NULL,\r
534         // INITIALIZE Ctr callers\r
535         PCD7004CtrRead,\r
536         PCD7004CtrReset,\r
537         // INITIALIZE PWM callers\r
538         PCD7004PWMWrite,            \r
539         NULL,                   // Not available\r
540         NULL,\r
541         // INITIALIZE Step callers\r
542         NULL,                       // Not available\r
543 };\r
544 \r