1/* @file plustek-pp_procfs.c 2 * @brief this is the interface to the proc filesystem 3 * 4 * Copyright (C) 2000-2013 Gerhard Jaeger <gerhard@gjaeger.de> 5 * 6 * History: 7 * - 0.37 - initial version 8 * - 0.38 - changes according to generic structure changes 9 * - 0.39 - added info about forceMode and slowIO 10 * - 0.40 - no changes 11 * - 0.41 - no changes 12 * - 0.42 - changed include names 13 * - 0.43 - replace _PTDRV_VERx by _PTDRV_VERSTR 14 * - cleanup 15 * - 0.44 - PROC_FS changes for newer kernel 16 * - fix format string issues, as Long types default to int32_t 17 * now 18 * . 19 * <hr> 20 * This file is part of the SANE package. 21 * 22 * This program is free software; you can redistribute it and/or 23 * modify it under the terms of the GNU General Public License as 24 * published by the Free Software Foundation; either version 2 of the 25 * License, or (at your option) any later version. 26 * 27 * This program is distributed in the hope that it will be useful, but 28 * WITHOUT ANY WARRANTY; without even the implied warranty of 29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 30 * General Public License for more details. 31 * 32 * You should have received a copy of the GNU General Public License 33 * along with this program. If not, see <https://www.gnu.org/licenses/>. 34 * 35 * As a special exception, the authors of SANE give permission for 36 * additional uses of the libraries contained in this release of SANE. 37 * 38 * The exception is that, if you link a SANE library with other files 39 * to produce an executable, this does not by itself cause the 40 * resulting executable to be covered by the GNU General Public 41 * License. Your use of that executable is in no way restricted on 42 * account of linking the SANE library code into it. 43 * 44 * This exception does not, however, invalidate any other reasons why 45 * the executable file might be covered by the GNU General Public 46 * License. 47 * 48 * If you submit changes to SANE to the maintainers to be included in 49 * a subsequent release, you agree by submitting the changes that 50 * those changes may be distributed with this exception intact. 51 * 52 * If you write modifications of your own for SANE, it is your choice 53 * whether to permit this exception to apply to your modifications. 54 * If you do not wish that, delete this exception notice. 55 * <hr> 56 */ 57#ifdef __KERNEL__ 58#include <linux/proc_fs.h> 59 60#include "plustek-pp_scan.h" 61 62/* toggled by your kernel configuration */ 63#ifdef CONFIG_PROC_FS 64 65/****************************** static vars **********************************/ 66 67/** for the proc filesystem 68 */ 69#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) 70extern struct proc_dir_entry proc_root; 71#endif 72static struct proc_dir_entry *base = NULL; 73static struct proc_dir_entry *binfo = NULL; 74static ULong devcount; 75 76/** parallel port modes... */ 77static char *procfsPortModes[] = { 78 "EPP", 79 "SPP", 80 "BiDi (PS/2)", 81 "ECP" 82 "unknown", 83 NULL 84}; 85 86/** CCD-Types (as for ASIC 98001 based series) */ 87static TabDef procfsCCDTypes98001[] = { 88 89 { _CCD_3797, "3797" }, 90 { _CCD_3717, "3717" }, 91 { _CCD_535, "535" }, 92 { _CCD_2556, "2556" }, 93 { _CCD_518, "518" }, 94 { _CCD_539, "539" }, 95 { -1 , "unknown" } 96}; 97 98/** CCD-Types (as for ASIC 98003 based series) */ 99static TabDef procfsCCDTypes98003[] = { 100 101 { _CCD_3797, "3797" }, 102 { _CCD_3799, "3799" }, 103 { _CCD_535, "535" }, 104 { _CCD_2556, "2556" }, 105 { _CCD_518, "518" }, 106 { _CCD_539, "539" }, 107 { _CCD_3777, "3777" }, 108 { _CCD_548 , "548" }, 109 { -1 , "unknown" } 110}; 111 112/****************************** local functions ******************************/ 113 114#ifndef LINUX_24 115/** This is called as the fill_inode function when an inode 116 * is going into (fill = 1) or out of service (fill = 0). 117 * 118 * Note: only the top-level directory needs to do this; if 119 * a lower level is referenced, the parent will be as well. 120 * 121 * Here simply a dummy function 122 */ 123static void procfsFillFunc( struct inode *inode, int fill ) 124{ 125} 126#endif 127 128/** returns a pointer to the port-mode string 129 */ 130static const char* procfsGetMode( int mode ) 131{ 132 if((mode < _PORT_EPP) || (mode > _PORT_ECP)) 133 return procfsPortModes[_PORT_ECP+1]; 134 135 return procfsPortModes[mode]; 136} 137 138/** determines CCD-Type string 139 */ 140static const char* procfsGetCCDType( pScanData ps ) 141{ 142 int i; 143 int ccd_id = ps->Device.bCCDID; 144 pTabDef tab = procfsCCDTypes98001; 145 146 if( _IS_ASIC98(ps->sCaps.AsicID)) { 147 148 if(_ASIC_IS_98003 == ps->sCaps.AsicID) 149 tab = procfsCCDTypes98003; 150 151 /* seek down the description table */ 152 for( i = 0; -1 != tab[i].id; i++ ) { 153 154 if( tab[i].id == ccd_id ) 155 return tab[i].desc; 156 } 157 } else { 158 159 /* for older scanners only this info is available */ 160 if( ps->fSonyCCD ) 161 return "SONY Type"; 162 else 163 return "NEC/TOSHIBA Type"; 164 } 165 166 /* return the last entry if nothing applies! */ 167 return tab[(sizeof(procfsCCDTypes98001)/sizeof(TabDef)-1)].desc; 168} 169 170/** will be called when reading the proc filesystem: 171 * cat /proc/pt_drv/info 172 */ 173static int procfsBInfoReadProc( char *buf, char **start, off_t offset, 174 int count, int *eof, void *data ) 175{ 176 int len = 0; 177 178 len += sprintf( buf, "Plustek Flatbed Scanner Driver version "_PTDRV_VERSTR"\n" ); 179 len += sprintf( buf + len, "IOCTL-Version: 0x%08x\n",_PTDRV_IOCTL_VERSION); 180 return len; 181} 182 183/** will be called when reading the proc filesystem: 184 * cat /proc/pt_drv/deviceX/info 185 */ 186static int procfsInfoReadProc( char *buf, char **start, off_t offset, 187 int count, int *eof, void *data ) 188{ 189 int len = 0; 190 pScanData ps = (pScanData)data; 191 192 /* Tell us something about the device... */ 193 if( NULL != ps ) { 194 len += sprintf( buf+len, "Model : %s\n", 195 MiscGetModelName(ps->sCaps.Model)); 196 len += sprintf( buf+len, "Portaddress : 0x%X\n", ps->IO.portBase ); 197 len += sprintf( buf+len, "Portmode : %s (%s I/O, %s)\n", 198 procfsGetMode(ps->IO.portMode), 199 (ps->IO.slowIO == _TRUE?"delayed":"fast"), 200 (ps->IO.forceMode == 0?"autodetect":"forced")); 201 len += sprintf( buf+len, "Buttons : %u\n", ps->Device.buttons); 202 len += sprintf( buf+len, "Warmuptime : %us\n", ps->warmup ); 203 len += sprintf( buf+len, "Lamp timeout: %us\n", ps->lampoff ); 204 len += sprintf( buf+len, "mov-switch : %u\n", ps->ModelOverride ); 205 len += sprintf( buf+len, "I/O-delay : %u\n", ps->IO.delay ); 206 len += sprintf( buf+len, "CCD-Type : %s\n", procfsGetCCDType(ps)); 207 len += sprintf( buf+len, "TPA : %s\n", 208 (ps->sCaps.dwFlag & SFLAG_TPA) ? "yes":"no" ); 209 } 210 211 return len; 212} 213 214/** will be called when reading the proc filesystem: 215 * cat /proc/pt_drv/devicex/buttony 216 */ 217static int procfsButtonsReadProc( char *buf, char **start, off_t offset, 218 int count, int *eof, void *data ) 219{ 220 Byte b; 221 int bc = 0; 222 int len = 0; 223 pScanData ps = (pScanData)data; 224 225 if( NULL != ps ) { 226 bc = ps->Device.buttons; 227 } 228 229 /* Check the buttons... */ 230 if( 0 != bc ) { 231 232 if ( _ASIC_IS_96003 == ps->sCaps.AsicID ) { 233 MiscClaimPort( ps ); 234 b = IODataRegisterFromScanner( ps, ps->RegStatus ); 235 if(_FLAG_P96_KEY == (b & _FLAG_P96_KEY)) 236 b = 0; 237 else 238 b = 1; 239 MiscReleasePort( ps ); 240 len += sprintf( buf + len, "%u\n", b ); 241 } else 242 bc = 0; 243 } 244 245 if( 0 == bc ) 246 len += sprintf( buf + len, "none\n" ); 247 248 return len; 249} 250 251/** create a procfs entry 252 */ 253static struct proc_dir_entry *new_entry( const char *name, mode_t mode, 254 struct proc_dir_entry *parent ) 255{ 256#ifndef LINUX_24 257 int len; 258#endif 259 struct proc_dir_entry *ent; 260 261 if (mode == S_IFDIR) 262 mode |= S_IRUGO | S_IXUGO; 263 else if (mode == 0) 264 mode = S_IFREG | S_IRUGO; 265 266#ifndef LINUX_24 267 len = strlen(name) + 1; 268 269 /* allocate memory for the entry and the name */ 270 ent = kmalloc(sizeof(struct proc_dir_entry) + len, GFP_KERNEL); 271 if( NULL == ent ) 272 return NULL; 273 274 memset(ent, 0, sizeof(struct proc_dir_entry)); 275 276 /* position pointer of name to end of the structure*/ 277 ent->name = ((char *) ent) + sizeof(*ent); 278 strcpy((char *)ent->name, name ); 279 280 ent->namelen = strlen(name); 281 ent->mode = mode; 282 283 if (S_ISDIR(mode)) { 284 ent->nlink = 2; 285 ent->fill_inode = &procfsFillFunc; 286 } else { 287 ent->nlink = 1; 288 } 289 290 proc_register( parent, ent ); 291#else 292 if (mode == S_IFDIR) 293 ent = proc_mkdir( name, parent ); 294 else 295 ent = create_proc_entry( name, mode, parent ); 296#endif 297 298 return ent; 299} 300 301/** shutdown one proc fs entry 302 */ 303static inline void destroy_proc_entry( struct proc_dir_entry *root, 304 struct proc_dir_entry **d ) 305{ 306#ifndef LINUX_24 307 proc_unregister( root, (*d)->low_ino ); 308 kfree(*d); 309#else 310 DBG(DBG_HIGH, "pt_drv: proc del '%s' root='%s'\n", (*d)->name, root->name); 311 312 remove_proc_entry((*d)->name, root ); 313#endif 314 315 *d = NULL; 316} 317 318/** shutdown the proc-tree for one device 319 */ 320static void destroy_proc_tree( pScanData ps ) 321{ 322 int i; 323 324 DBG( DBG_HIGH, "pt_drv: destroy_proc_tree !\n" ); 325 326 if( ps ) { 327 328 if( ps->procDir.entry ) { 329 330 if( ps->procDir.info ) 331 destroy_proc_entry( ps->procDir.entry, &ps->procDir.info ); 332 333 for( i = 0; i < ps->Device.buttons; i++ ) { 334 335 if( ps->procDir.buttons[i] ) 336 destroy_proc_entry(ps->procDir.entry, &ps->procDir.buttons[i]); 337 } 338 339 destroy_proc_entry( base, &ps->procDir.entry ); 340 } 341 } 342} 343 344/*************************** exported functions ******************************/ 345 346/** initialize our proc-fs stuff 347 */ 348int ProcFsInitialize( void ) 349{ 350 DBG( DBG_HIGH, "ProcFsInitialize()\n" ); 351 352#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) 353 base = new_entry( _DRV_NAME, S_IFDIR, &proc_root ); 354#else 355 base = new_entry( _DRV_NAME, S_IFDIR, NULL ); 356#endif 357 358 if( NULL != base ) { 359 360 devcount = 0; 361 362 binfo = new_entry( "info", 0, base ); 363 if( NULL != binfo ) { 364 binfo->read_proc = procfsBInfoReadProc; 365 binfo->data = &devcount; 366 } 367 } 368 369 return _OK; 370} 371 372/** cleanup the base entry 373 */ 374void ProcFsShutdown( void ) 375{ 376 DBG( DBG_HIGH, "ProcFsShutdown()\n" ); 377 378 if( NULL != base ) { 379 380 if( NULL != binfo ) 381 destroy_proc_entry( base, &binfo ); 382 383#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) 384 destroy_proc_entry( &proc_root, &base ); 385#else 386 destroy_proc_entry( NULL, &base ); 387#endif 388 } 389 390 devcount = 0; 391} 392 393/** will be called for each device, that has been found 394 */ 395void ProcFsRegisterDevice( pScanData ps ) 396{ 397 int i; 398 char str[20]; 399 400 if( NULL == base ) { 401 printk( KERN_ERR "pt_drv : proc not initialised yet!\n"); 402 return; 403 } 404 405 memset( &ps->procDir, 0, sizeof(ProcDirDef)); 406 407 sprintf( str, "device%u", ps->devno ); 408 409 ps->procDir.entry = new_entry( str, S_IFDIR, base ); 410 if( NULL == ps->procDir.entry ) 411 goto error_exit; 412 413 ps->procDir.info = new_entry( "info", 0, ps->procDir.entry ); 414 if( NULL == ps->procDir.info ) 415 goto error_exit; 416 417 ps->procDir.info->read_proc = procfsInfoReadProc; 418 ps->procDir.info->data = ps; 419 420 for( i = 0; i < ps->Device.buttons; i++ ) { 421 422 sprintf( str, "button%u", i ); 423 424 ps->procDir.buttons[i] = new_entry( str, 0, ps->procDir.entry ); 425 if( NULL == ps->procDir.buttons[i] ) 426 goto error_exit; 427 428 ps->procDir.buttons[i]->read_proc = procfsButtonsReadProc; 429 ps->procDir.buttons[i]->data = ps; 430 } 431 432 devcount++; 433 return; 434 435 436error_exit: 437 438 printk(KERN_ERR "pt_drv: failure registering /proc/ entry %s.\n", str ); 439 destroy_proc_tree( ps ); 440} 441 442/** cleanup the proc-fs for a certain device 443 */ 444void ProcFsUnregisterDevice( pScanData ps ) 445{ 446 destroy_proc_tree( ps ); 447} 448 449#else /* CONFIG_PROC_FS */ 450 451int ProcFsInitialize( void ) 452{ 453 return _OK; 454} 455 456void ProcFsShutdown( void ) 457{ 458} 459 460void ProcFsRegisterDevice( pScanData ps ) 461{ 462} 463 464void ProcFsUnregisterDevice( pScanData ps ) 465{ 466} 467 468#endif 469 470#endif /* guard __KERNEL__ */ 471 472/* END PLUSTEK-PP_PROCFS.C ..................................................*/ 473