unit kern_conf; {$mode ObjFPC}{$H+} {$CALLING SysV_ABI_CDecl} interface uses mqueue, sys_event, vmount, vfile, vuio, vnode, time, vm, vm_object, kern_mtx; const //modeventtype MOD_LOAD =0; MOD_UNLOAD =1; MOD_SHUTDOWN=2; MOD_QUIESCE =3; SI_ETERNAL =$0001; { never destroyed } SI_ALIAS =$0002; { carrier of alias name } SI_NAMED =$0004; { make_dev _alias has been called } SI_CHEAPCLONE=$0008; { can be removed_dev'ed when vnode reclaims } SI_CHILD =$0010; { child of another struct cdev *} SI_DEVOPEN =$0020; { opened by device } SI_CONSOPEN =$0040; { opened by console } SI_DUMPDEV =$0080; { is kernel dumpdev } SI_CANDELETE =$0100; { can do BIO_DELETE } SI_CLONELIST =$0200; { on a clone list } SI_UNMAPPED =$0400; { can handle unmapped I/O } SPECNAMELEN =63; { max length of devicename } type pp_cdev=^p_cdev; p_cdev=^t_cdev; t_cdev=packed record si_mountpt :p_mount; si_flags :DWORD; _align1 :Integer; si_atime :timespec; si_ctime :timespec; si_mtime :timespec; si_uid :uid_t; si_gid :gid_t; si_mode :mode_t; si_drv0 :Integer; si_refcount :Integer; _align2 :Integer; si_list :LIST_ENTRY; //(cdev) si_clone :LIST_ENTRY; //(cdev) si_children :LIST_HEAD ; //(cdev) si_siblings :LIST_ENTRY; //(cdev) si_parent :p_cdev; si_name :PChar; si_drv1 :Pointer; si_drv2 :Pointer; si_devsw :Pointer; //cdevsw si_iosize_max :Integer; { maximum I/O size (for physio &al) } _align3 :Integer; si_usecount :QWORD; si_threadcount:QWORD; si_snapdata :Pointer; //snapdata __si_namebuf :array[0..SPECNAMELEN] of AnsiChar; end; d_open_t =Function (dev:p_cdev;oflags,devtype:Integer):Integer; d_fdopen_t =Function (dev:p_cdev;oflags:Integer;fp:p_file):Integer; d_close_t =Function (dev:p_cdev;fflag,devtype:Integer):Integer; d_strategy_t =Procedure(bp:Pointer);//bio d_ioctl_t =Function (dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer; d_read_t =Function (dev:p_cdev;uio:p_uio;ioflag:Integer):Integer; d_write_t =Function (dev:p_cdev;uio:p_uio;ioflag:Integer):Integer; d_poll_t =Function (dev:p_cdev;events:Integer):Integer; d_kqfilter_t =Function (dev:p_cdev;kn:p_knote):Integer; d_mmap_t =Function (dev:p_cdev;offset:vm_ooffset_t;paddr:p_vm_paddr_t;nprot:Integer;memattr:p_vm_memattr_t):Integer; d_mmap_single_t=Function (cdev:p_cdev;offset:p_vm_ooffset_t;size:vm_size_t;obj:p_vm_object_t;nprot:Integer):Integer; d_purge_t =Procedure(dev:p_cdev); dumper_t=Function( _priv:Pointer ; { Private to the driver. } _virtual:Pointer ; { Virtual (mapped) address. } _physical:vm_offset_t; { Physical address of virtual. } _offset:Int64 ; { Byte-offset to write at. } _length:QWORD):Integer; { Number of bytes to dump. } const { * Types for d_flags. } D_TAPE=$0001; D_DISK=$0002; D_TTY =$0004; D_MEM =$0008; D_TYPEMASK=$ffff; { * Flags for d_flags which the drivers can set. } D_TRACKCLOSE =$00080000; { track all closes } D_MMAP_ANON =$00100000; { special treatment in vm_mmap.c } D_PSEUDO =$00200000; { make_dev() can return NULL } D_NEEDGIANT =$00400000; { driver want Giant } D_NEEDMINOR =$00800000; { driver uses clone_create() } D_UNMAPPED_IO=$01000000; { d_strategy can accept unmapped IO } { * Version numbers. } D_VERSION_00=$20011966; D_VERSION_01=$17032005; { Add d_uid,gid,mode & kind } D_VERSION_02=$28042009; { Add d_mmap_single } D_VERSION_03=$17122009; { d_mmap takes memattr,vm_ooffset_t } D_VERSION=D_VERSION_03; { * Flags used for internal housekeeping } D_INIT =$80000000; { cdevsw initialized } { * Character device switch table } type pp_cdevsw=^p_cdevsw; p_cdevsw=^t_cdevsw; t_cdevsw=packed record d_version :Integer; d_flags :DWORD; d_name :PChar; d_open :d_open_t; d_fdopen :d_fdopen_t; d_close :d_close_t; d_read :d_read_t; d_write :d_write_t; d_ioctl :d_ioctl_t; d_poll :d_poll_t; d_mmap :d_mmap_t; d_strategy :d_strategy_t; d_dump :dumper_t; d_kqfilter :d_kqfilter_t; d_purge :d_purge_t; d_mmap_single:d_mmap_single_t; d_spare0:array[0..3] of Integer; d_spare1:array[0..2] of Pointer; { These fields should not be messed with by drivers } d_devs:Pointer; //cdev d_spare2:array[0..1] of Integer; d_gianttrick:p_cdevsw; d_postfree_list:SLIST_ENTRY; //cdevsw end; const MAKEDEV_REF =$01; MAKEDEV_WHTOUT =$02; MAKEDEV_NOWAIT =$04; MAKEDEV_WAITOK =$08; MAKEDEV_ETERNAL =$10; MAKEDEV_CHECKNAME=$20; UID_ROOT =0; UID_BIN =3; UID_UUCP =66; UID_NOBODY =65534; GID_WHEEL =0; GID_KMEM =2; GID_TTY =4; GID_OPERATOR=5; GID_BIN =7; GID_GAMES =13; GID_DIALER =68; GID_NOBODY =65534; function dev2unit(d:p_cdev):Integer; inline; function devtoname(dev:p_cdev):PChar; type cdevpriv_dtr_t=Procedure(data:Pointer); var devmtx:mtx; cdevp_free_list:TAILQ_HEAD=(tqh_first:nil;tqh_last:@cdevp_free_list.tqh_first); cdevsw_gt_post_list:SLIST_HEAD=(slh_first:nil); function _nullop():Integer; function _eopnotsupp():Integer; function _enxio():Integer; function _enodev():Integer; function _einval():Integer; procedure dev_lock(); procedure dev_unlock(); procedure dev_ref(dev:p_cdev); procedure dev_refl(dev:p_cdev); procedure dev_rel(dev:p_cdev); function dev_refthread(dev:p_cdev;ref:PInteger):p_cdevsw; function devvn_refthread(vp:p_vnode;devp:pp_cdev;ref:PInteger):p_cdevsw; procedure dev_relthread(dev:p_cdev;ref:Integer); procedure destroy_dev(dev:p_cdev); function make_dev_credv(flags:Integer; dres:pp_cdev; devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):Integer; register; function make_dev(devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):p_cdev; register; function make_dev_cred(devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):p_cdev; register; function make_dev_credf(flags:Integer; devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):p_cdev; register; function make_dev_p(flags:Integer; cdev:pp_cdev; devsw:p_cdevsw; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):Integer; register; function make_dev_alias_v(flags:Integer; cdev:pp_cdev; pdev:p_cdev; fmt:PChar; const Args:Array of const):Integer; register; function make_dev_alias(pdev:p_cdev; fmt:PChar; const Args:Array of const):p_cdev; register; function make_dev_alias_p(flags:Integer; cdev:pp_cdev; pdev:p_cdev; fmt:PChar; const Args:Array of const):Integer; register; function make_dev_physpath_alias(flags:Integer; cdev:pp_cdev; pdev,old_alias:p_cdev; physpath:PChar):Integer; implementation uses sysutils, errno, devfs, devfs_devs, devfs_vnops, vsys_generic, kern_synch; function dev2unit(d:p_cdev):Integer; inline; begin Result:=d^.si_drv0; end; function devtoname(dev:p_cdev):PChar; inline; begin Result:=dev^.si_name; end; function cdev2priv(c:Pointer):p_cdev_priv; inline; begin Result:=c-ptruint(@p_cdev_priv(nil)^.cdp_c); end; procedure dev_lock(); begin mtx_lock(devmtx); end; procedure dev_unlock(); begin mtx_unlock(devmtx); end; procedure dev_ref(dev:p_cdev); begin mtx_lock(devmtx); Inc(dev^.si_refcount); mtx_unlock(devmtx); end; procedure dev_refl(dev:p_cdev); begin mtx_assert(devmtx); Inc(dev^.si_refcount); end; procedure dev_rel(dev:p_cdev); var flag:Integer; begin flag:=0; dev_lock(); Dec(dev^.si_refcount); Assert(dev^.si_refcount >= 0,'dev_rel(%s) gave negative count'); if (dev^.si_devsw=nil) and (dev^.si_refcount=0) then begin LIST_REMOVE(dev,@dev^.si_list); flag:=1; end; dev_unlock(); if (flag<>0) then devfs_free(dev); end; function dev_refthread(dev:p_cdev;ref:PInteger):p_cdevsw; var csw:p_cdevsw; cdp:p_cdev_priv; begin if ((dev^.si_flags and SI_ETERNAL)<>0) then begin ref^:=0; Exit(dev^.si_devsw); end; dev_lock(); csw:=dev^.si_devsw; if (csw<>nil) then begin cdp:=cdev2priv(dev); if ((cdp^.cdp_flags and CDP_SCHED_DTR)=0) then Inc(dev^.si_threadcount) else csw:=nil; end; dev_unlock(); ref^:=1; Exit(csw); end; function devvn_refthread(vp:p_vnode;devp:pp_cdev;ref:PInteger):p_cdevsw; var csw:p_cdevsw; cdp:p_cdev_priv; dev:p_cdev; begin if ((vp^.v_vflag and VV_ETERNALDEV)<>0) then begin dev:=vp^.v_rdev; if (dev=nil) then Exit(nil); Assert((dev^.si_flags and SI_ETERNAL)<>0,'Not eternal cdev'); ref^:=0; csw:=dev^.si_devsw; Assert(csw<>nil,'Eternal cdev is destroyed'); devp^:=dev; Exit(csw); end; csw:=nil; dev_lock(); dev:=vp^.v_rdev; if (dev=nil) then begin dev_unlock(); Exit(nil); end; cdp:=cdev2priv(dev); if ((cdp^.cdp_flags and CDP_SCHED_DTR)=0) then begin csw:=dev^.si_devsw; if (csw<>nil) then Inc(dev^.si_threadcount); end; dev_unlock(); if (csw<>nil) then begin devp^:=dev; ref^:=1; end; Exit(csw); end; procedure dev_relthread(dev:p_cdev;ref:Integer); begin if (ref=0) then Exit; dev_lock(); Assert(dev^.si_threadcount > 0,'%s threadcount is wrong'); Dec(dev^.si_threadcount); dev_unlock(); end; { * Free all the memory collected while the cdev mutex was * locked. Since devmtx is after the system map mutex, free() cannot * be called immediately and is postponed until cdev mutex can be * dropped. } procedure dev_unlock_and_free(); var cdp_free:TAILQ_HEAD; csw_free:SLIST_HEAD; cdp:p_cdev_priv; csw:p_cdevsw; begin mtx_assert(devmtx); { * Make the local copy of the list heads while the dev_mtx is * held. Free it later. } TAILQ_INIT(@cdp_free); TAILQ_CONCAT(@cdp_free,@cdevp_free_list,@p_cdev_priv(nil)^.cdp_list); csw_free:=cdevsw_gt_post_list; SLIST_INIT(@cdevsw_gt_post_list); mtx_unlock(devmtx); cdp:=TAILQ_FIRST(@cdp_free); while (cdp<>nil) do begin TAILQ_REMOVE(@cdp_free,cdp,@cdp^.cdp_list); devfs_free(@cdp^.cdp_c); cdp:=TAILQ_FIRST(@cdp_free); end; csw:=SLIST_FIRST(@csw_free); while (csw<>nil) do begin SLIST_REMOVE_HEAD(@csw_free,@p_cdevsw(nil)^.d_postfree_list); FreeMem(csw); csw:=SLIST_FIRST(@csw_free); end; end; procedure dev_free_devlocked(cdev:p_cdev); var cdp:p_cdev_priv; begin mtx_assert(devmtx); cdp:=cdev2priv(cdev); Assert((cdp^.cdp_flags and CDP_UNREF_DTR)=0,'destroy_dev() was not called after delist_dev(%p)'); TAILQ_INSERT_HEAD(@cdevp_free_list,cdp,@cdp^.cdp_list); end; procedure cdevsw_free_devlocked(csw:p_cdevsw); begin mtx_assert(devmtx); SLIST_INSERT_HEAD(@cdevsw_gt_post_list,csw,@csw^.d_postfree_list); end; function _nullop():Integer; begin Exit(0); end; function _eopnotsupp():Integer; begin Exit(EOPNOTSUPP); end; function _enxio():Integer; begin Exit(ENXIO); end; function _enodev():Integer; begin Exit(ENODEV); end; function _einval():Integer; begin Exit(EINVAL); end; procedure dead_strategy(bp:Pointer); begin //biofinish(bp, nil, ENXIO); end; const dead_cdevsw:t_cdevsw=( d_version :D_VERSION; d_flags :0; d_name :'dead'; d_open :d_open_t(@_enxio); d_fdopen :nil; d_close :d_close_t(@_enxio); d_read :d_read_t(@_enxio); d_write :d_write_t(@_enxio); d_ioctl :d_ioctl_t(@_enxio); d_poll :d_poll_t(@_enodev); d_mmap :d_mmap_t(@_enodev); d_strategy :@dead_strategy; d_dump :dumper_t(@_enxio); d_kqfilter :d_kqfilter_t(@_enxio); d_purge :nil; d_mmap_single:d_mmap_single_t(@_enodev); ); procedure no_strategy(bp:Pointer); begin //biofinish(bp, nil, ENODEV); end; function no_poll(dev:p_cdev;events:Integer):Integer; begin Exit(poll_no_poll(events)); end; function giant_open(dev:p_cdev;oflags,devtype:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_open(dev, oflags, devtype); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; function giant_fdopen(dev:p_cdev;oflags:Integer;fp:p_file):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_fdopen(dev, oflags, fp); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; function giant_close(dev:p_cdev;fflag,devtype:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_close(dev, fflag, devtype); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; procedure giant_strategy(bp:Pointer); var dsw:p_cdevsw; dev:p_cdev; ref:Integer; begin //dev:=bp^.bio_dev; //dsw:=dev_refthread(dev, @ref); //if (dsw=nil) then //begin // biofinish(bp, nil, ENXIO); // Exit; //end; //mtx_lock(VFS_Giant); //dsw^.d_gianttrick^.d_strategy(bp); //mtx_unlock(VFS_Giant); //dev_relthread(dev, ref); end; function giant_ioctl(dev:p_cdev;cmd:QWORD;data:Pointer;fflag:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_ioctl(dev, cmd, data, fflag); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; function giant_read(dev:p_cdev;uio:p_uio;ioflag:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_read(dev, uio, ioflag); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; function giant_write(dev:p_cdev;uio:p_uio;ioflag:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_write(dev, uio, ioflag); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; function giant_poll(dev:p_cdev;events:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_poll(dev, events); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; function giant_kqfilter(dev:p_cdev;kn:Pointer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_kqfilter(dev, kn); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; Function giant_mmap(dev:p_cdev;offset:vm_ooffset_t;paddr:p_vm_paddr_t;nprot:Integer;memattr:p_vm_memattr_t):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_mmap(dev, offset, paddr, nprot, memattr); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; Function giant_mmap_single(dev:p_cdev;offset:p_vm_ooffset_t;size:vm_size_t;obj:p_vm_object_t;nprot:Integer):Integer; var dsw:p_cdevsw; ref,retval:Integer; begin dsw:=dev_refthread(dev, @ref); if (dsw=nil) then begin Exit(ENXIO); end; mtx_lock(VFS_Giant); retval:=dsw^.d_gianttrick^.d_mmap_single(dev, offset, size, obj, nprot); mtx_unlock(VFS_Giant); dev_relthread(dev, ref); Exit(retval); end; procedure notify(dev:p_cdev;ev:PChar;flags:Integer); const prefix:PChar='cdev='; var data:PChar; namelen:Integer; begin //if (cold) then Exit; namelen:=strlen(dev^.si_name); data:=AllocMem(namelen + sizeof(prefix)); if (data=nil) then Exit; Move(prefix^, data^, sizeof(prefix) - 1); Move(dev^.si_name^, (data + sizeof(prefix) - 1)^, namelen + 1); //devctl_notify_f('DEVFS', 'CDEV', ev, data, mflags); FreeMem(data); end; procedure notify_create(dev:p_cdev;flags:Integer); begin notify(dev, 'CREATE', flags); end; procedure notify_destroy(dev:p_cdev); begin notify(dev, 'DESTROY', MAKEDEV_WAITOK); end; function newdev(csw:p_cdevsw;_unit:Integer;si:p_cdev):p_cdev; var si2:p_cdev; begin mtx_assert(devmtx); if ((csw^.d_flags and D_NEEDMINOR)<>0) then begin { We may want to Exitan existing device } si2:=LIST_FIRST(@csw^.d_devs); while (si2<>nil) do begin if (dev2unit(si2)=_unit) then begin dev_free_devlocked(si); Exit(si2); end; si2:=LIST_NEXT(si2,@si2^.si_list); end; end; si^.si_drv0 :=_unit; si^.si_devsw:=csw; LIST_INSERT_HEAD(@csw^.d_devs,si,@si^.si_list); Exit(si); end; procedure fini_cdevsw(devsw:p_cdevsw); var gt:p_cdevsw; begin if (devsw^.d_gianttrick<>nil) then begin gt:=devsw^.d_gianttrick; Move(gt^, devsw^, sizeof(t_cdevsw)); cdevsw_free_devlocked(gt); devsw^.d_gianttrick:=nil; end; devsw^.d_flags:=devsw^.d_flags and (not D_INIT); end; function prep_cdevsw(devsw:p_cdevsw;flags:Integer):Integer; var dsw2:p_cdevsw; procedure FIXUP(member:PPointer;noop,giant:Pointer); inline; begin if (member^=nil) then begin member^:=noop; end else if ((devsw^.d_flags and D_NEEDGIANT)<>0) then begin member^:=giant; end; end; begin mtx_assert(devmtx); if ((devsw^.d_flags and D_INIT)<>0) then begin Exit(0); end; if ((devsw^.d_flags and D_NEEDGIANT)<>0) then begin dev_unlock(); dsw2:=AllocMem(sizeof(t_cdevsw)); dev_lock(); if (dsw2=nil) and ((devsw^.d_flags and D_INIT)=0) then begin Exit(ENOMEM); end; end else dsw2:=nil; if ((devsw^.d_flags and D_INIT)<>0) then begin if (dsw2<>nil) then begin cdevsw_free_devlocked(dsw2); end; Exit(0); end; if (devsw^.d_version<>D_VERSION_03) then begin Writeln('WARNING: Device driver has wrong version'); devsw^.d_open :=d_open_t(@_enxio); devsw^.d_close :=d_close_t(@_enxio); devsw^.d_read :=d_read_t(@_enxio); devsw^.d_write :=d_write_t(@_enxio); devsw^.d_ioctl :=d_ioctl_t(@_enxio); devsw^.d_poll :=d_poll_t(@_enodev); devsw^.d_mmap :=d_mmap_t(@_enodev); devsw^.d_mmap_single:=d_mmap_single_t(@_enodev); devsw^.d_strategy :=@dead_strategy; devsw^.d_dump :=dumper_t(@_enxio); devsw^.d_kqfilter :=d_kqfilter_t(@_enxio); end; if ((devsw^.d_flags and D_NEEDGIANT)<>0) then begin if (devsw^.d_gianttrick=nil) then begin Move(devsw^, dsw2^, sizeof(t_cdevsw)); devsw^.d_gianttrick:=dsw2; dsw2:=nil; end; end; FIXUP(@devsw^.d_open, @_nullop, @giant_open); FIXUP(@devsw^.d_fdopen, nil, @giant_fdopen); FIXUP(@devsw^.d_close, @_nullop, @giant_close); FIXUP(@devsw^.d_read, @_enodev, @giant_read); FIXUP(@devsw^.d_write, @_enodev, @giant_write); FIXUP(@devsw^.d_ioctl, @_enodev, @giant_ioctl); FIXUP(@devsw^.d_poll, @no_poll, @giant_poll); FIXUP(@devsw^.d_mmap, @_enodev, @giant_mmap); FIXUP(@devsw^.d_strategy, @no_strategy, @giant_strategy); FIXUP(@devsw^.d_kqfilter, @_enodev, @giant_kqfilter); FIXUP(@devsw^.d_mmap_single, @_enodev, @giant_mmap_single); if (devsw^.d_dump=nil) then devsw^.d_dump:=dumper_t(@_enodev); LIST_INIT(@devsw^.d_devs); devsw^.d_flags:=devsw^.d_flags or D_INIT; if (dsw2<>nil) then cdevsw_free_devlocked(dsw2); Exit(0); end; function prep_devname(dev:p_cdev;fmt:PChar;const Args:Array of const):Integer; register; var R:RawByteString; len:Integer; from,q,s,_to:PChar; begin mtx_assert(devmtx); R:=Format(fmt,Args); len:=Length(R); if (len > sizeof(dev^.__si_namebuf) - 1) then begin Exit(ENAMETOOLONG); end; Move(PChar(R)^,dev^.__si_namebuf,len); { Strip leading slashes. } from:=@dev^.__si_namebuf; while (from='/') do Inc(from); _to:=@dev^.__si_namebuf; while (from^<>#0) do begin { Treat multiple sequential slashes as single. } while (from[0]='/') and (from[1]='/') do begin Inc(from); end; { Trailing slash is considered invalid. } if (from[0]='/') and (from[1]=#0) then begin Exit(EINVAL); end; _to^:=from^; // Inc(from); Inc(_to); end; _to^:=#0; if (dev^.__si_namebuf[0]=#0) then begin Exit(EINVAL); end; { Disallow '.' and '..' components. } s:=@dev^.__si_namebuf; while true do begin q:=s; while (q^<>'/') and (q^<>#0) do Inc(q); if (q - s=1) and (s[0]='.') then begin Exit(EINVAL); end; if (q - s=2) and (s[0]='.') and (s[1]='.') then begin Exit(EINVAL); end; if (q^<>'/') then begin break; end; s:=q + 1; end; if (devfs_dev_exists(dev^.__si_namebuf)<>0) then begin Exit(EEXIST); end; Exit(0); end; function make_dev_credv(flags:Integer; dres:pp_cdev; devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):Integer; register; var dev,dev_new:p_cdev; res:Integer; begin Assert(((flags and MAKEDEV_WAITOK)=0) or ((flags and MAKEDEV_NOWAIT)=0),'make_dev_credv: both WAITOK and NOWAIT specified'); dev_new:=devfs_alloc(flags); if (dev_new=nil) then begin Exit(ENOMEM); end; dev_lock(); res:=prep_cdevsw(devsw, flags); if (res<>0) then begin dev_unlock(); devfs_free(dev_new); Exit(res); end; dev:=newdev(devsw, _unit, dev_new); if ((dev^.si_flags and SI_NAMED)=0) then begin res:=prep_devname(dev, fmt, Args); if (res<>0) then begin if ((flags and MAKEDEV_CHECKNAME)=0) then begin Assert(False,'make_dev_credv: bad si_name (error=%d, si_name=%s)'); end; if (dev=dev_new) then begin LIST_REMOVE(dev,@dev^.si_list); dev_unlock(); devfs_free(dev); end else dev_unlock(); Exit(res); end; end; if ((flags and MAKEDEV_REF)<>0) then begin dev_refl(dev); end; if ((flags and MAKEDEV_ETERNAL)<>0) then dev^.si_flags:=dev^.si_flags or SI_ETERNAL; if ((dev^.si_flags and SI_CHEAPCLONE)<>0) and ((dev^.si_flags and SI_NAMED)<>0) then begin { * This is allowed as it removes races and generally * simplifies cloning devices. * XXX: still ?? } dev_unlock_and_free(); dres^:=dev; Exit(0); end; Assert((dev^.si_flags and SI_NAMED)=0,'make_dev() by driver %s on pre-existing device (min=%x, name=%s)'); dev^.si_flags:=dev^.si_flags or SI_NAMED; dev^.si_uid :=uid; dev^.si_gid :=gid; dev^.si_mode:=mode; devfs_create(dev); //clean_unrhdrl(devfs_inos); dev_unlock_and_free(); notify_create(dev, flags); dres^:=dev; Exit(0); end; function make_dev(devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):p_cdev; register; var dev:p_cdev; res:Integer; begin res:=make_dev_credv(0, @dev, devsw, _unit, uid, gid, mode, fmt, Args); Assert((res=0) and (dev<>nil),'make_dev: failed make_dev_credv (error=%d)'); if (res=0) then Exit(dev) else Exit(nil); end; function make_dev_cred(devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):p_cdev; register; var dev:p_cdev; res:Integer; begin res:=make_dev_credv(0, @dev, devsw, _unit, uid, gid, mode, fmt, Args); Assert((res=0) and (dev<>nil),'make_dev_cred: failed make_dev_credv (error=%d)'); if (res=0) then Exit(dev) else Exit(nil); end; function make_dev_credf(flags:Integer; devsw:p_cdevsw; _unit:Integer; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):p_cdev; register; var dev:p_cdev; res:Integer; begin res:=make_dev_credv(flags, @dev, devsw, _unit, uid, gid, mode, fmt, Args); Assert((((flags and MAKEDEV_NOWAIT)<>0) and (res=ENOMEM)) or (((flags and MAKEDEV_CHECKNAME)<>0) and (res<>ENOMEM)) or (res=0),'make_dev_credf: failed make_dev_credv (error=%d)'); if (res=0) then Exit(dev) else Exit(nil); end; function make_dev_p(flags:Integer; cdev:pp_cdev; devsw:p_cdevsw; uid:uid_t; gid:gid_t; mode:Integer; fmt:PChar; const Args:Array of const):Integer; register; var res:Integer; begin res:=make_dev_credv(flags, cdev, devsw, 0, uid, gid, mode, fmt, Args); Assert((((flags and MAKEDEV_NOWAIT)<>0) and (res=ENOMEM)) or (((flags and MAKEDEV_CHECKNAME)<>0) and (res<>ENOMEM)) or (res=0),'make_dev_p: failed make_dev_credv (error=%d)'); Exit(res); end; procedure dev_dependsl(pdev,cdev:p_cdev); begin cdev^.si_parent:=pdev; cdev^.si_flags :=cdev^.si_flags or SI_CHILD; LIST_INSERT_HEAD(@pdev^.si_children,cdev,@cdev^.si_siblings); end; procedure dev_depends(pdev,cdev:p_cdev); begin dev_lock(); dev_dependsl(pdev, cdev); dev_unlock(); end; function make_dev_alias_v(flags:Integer; cdev:pp_cdev; pdev:p_cdev; fmt:PChar; const Args:Array of const):Integer; register; var dev:p_cdev; error:Integer; begin Assert(pdev<>nil,'make_dev_alias_v: pdev is nil'); Assert(((flags and MAKEDEV_WAITOK)=0) or ((flags and MAKEDEV_NOWAIT)=0),'make_dev_alias_v: both WAITOK and NOWAIT specified'); Assert((flags and (not (MAKEDEV_WAITOK or MAKEDEV_NOWAIT or MAKEDEV_CHECKNAME)))=0,'make_dev_alias_v: invalid flags specified (flags=%02x)'); dev:=devfs_alloc(flags); if (dev=nil) then begin Exit(ENOMEM); end; dev_lock(); dev^.si_flags:=dev^.si_flags or SI_ALIAS; error:=prep_devname(dev, fmt, Args); if (error<>0) then begin if ((flags and MAKEDEV_CHECKNAME)=0) then begin Assert(False,'make_dev_alias_v: bad si_name'); end; dev_unlock(); devfs_free(dev); Exit(error); end; dev^.si_flags:=dev^.si_flags or SI_NAMED; devfs_create(dev); dev_dependsl(pdev, dev); //clean_unrhdrl(devfs_inos); dev_unlock(); notify_create(dev, flags); cdev^:=dev; Exit(0); end; function make_dev_alias(pdev:p_cdev; fmt:PChar; const Args:Array of const):p_cdev; register; var dev:p_cdev; res:Integer; begin res:=make_dev_alias_v(MAKEDEV_WAITOK, @dev, pdev, fmt, Args); Assert((res=0) and (dev<>nil),'make_dev_alias: failed make_dev_alias_v (error=%d)'); if (res=0) then Exit(dev) else Exit(nil); end; function make_dev_alias_p(flags:Integer; cdev:pp_cdev; pdev:p_cdev; fmt:PChar; const Args:Array of const):Integer; register; var res:Integer; begin res:=make_dev_alias_v(flags, cdev, pdev, fmt, Args); Exit(res); end; function make_dev_physpath_alias(flags:Integer; cdev:pp_cdev; pdev,old_alias:p_cdev; physpath:PChar):Integer; label _ret, _out; var devfspath:PChar; physpath_len:Integer; max_parentpath_len:Integer; parentpath_len:Integer; devfspathbuf_len:Integer; ret:Integer; R:RawByteString; begin cdev^:=nil; devfspath:=nil; physpath_len:=strlen(physpath); ret:=EINVAL; if (physpath_len=0) then goto _out; if (strlcomp('id1,', physpath, 4)=0) then begin Inc(physpath,4); Dec(physpath_len,4); if (physpath_len=0) then begin goto _out; end; end; max_parentpath_len:=SPECNAMELEN - physpath_len - 1; parentpath_len:=strlen(pdev^.si_name); if (max_parentpath_len < parentpath_len) then begin ret:=ENAMETOOLONG; goto _out; end; devfspathbuf_len:=physpath_len + 1 + parentpath_len + 1; devfspath:=AllocMem(devfspathbuf_len); if (devfspath=nil) then begin ret:=ENOMEM; goto _out; end; R:=Format('%s/%s',[physpath,pdev^.si_name]); Move(PChar(R)^,devfspath^,Length(R)+1); if (old_alias=nil) then goto _ret; if (strcomp(old_alias^.si_name, devfspath)=0) then begin _ret: { Retain the existing alias. } cdev^:=old_alias; old_alias:=nil; ret:=0; end else begin ret:=make_dev_alias_p(flags, cdev, pdev, '%s', [devfspath]); end; _out: if (old_alias<>nil) then begin destroy_dev(old_alias); end; if (devfspath<>nil) then begin FreeMem(devfspath); end; Exit(ret); end; procedure destroy_devl(dev:p_cdev); var csw:p_cdevsw; p:p_cdev_privdata; cdp:p_cdev_priv; begin mtx_assert(devmtx); Assert((dev^.si_flags and SI_NAMED)<>0 ,'WARNING: Driver mistake: destroy_dev on %dn'); Assert((dev^.si_flags and SI_ETERNAL)=0,'WARNING: Driver mistake: destroy_dev on eternal %dn'); cdp:=cdev2priv(dev); if ((cdp^.cdp_flags and CDP_UNREF_DTR)=0) then begin { * Avoid race with dev_rel(), e.g. from the populate * loop. If CDP_UNREF_DTR flag is set, the reference * to be dropped at the end of destroy_devl() was * already taken by delist_dev_locked(). } dev_refl(dev); devfs_destroy(dev); end; { Remove name marking } dev^.si_flags:=dev^.si_flags and (not SI_NAMED); { If we are a child, remove us from the parents list } if ((dev^.si_flags and SI_CHILD)<>0) then begin LIST_REMOVE(dev,@dev^.si_siblings); dev^.si_flags:=dev^.si_flags and (not SI_CHILD); end; { Kill our children } while (not LIST_EMPTY(@dev^.si_children)) do begin destroy_devl(LIST_FIRST(@dev^.si_children)); end; { Remove from clone list } if ((dev^.si_flags and SI_CLONELIST)<>0) then begin LIST_REMOVE(dev,@dev^.si_clone); dev^.si_flags:=dev^.si_flags and (not SI_CLONELIST); end; csw:=dev^.si_devsw; dev^.si_devsw:=nil; { already nil for SI_ALIAS } while (csw<>nil) and (dev^.si_threadcount<>0) do begin if (csw^.d_purge=nil) then Break; csw^.d_purge(dev); msleep(csw, @devmtx, PRIBIO, 'devprg', hz div 10); if (dev^.si_threadcount<>0) then Writeln(Format('Still %lu threads in %sn',[dev^.si_threadcount, devtoname(dev)])); end; while (dev^.si_threadcount<>0) do begin { Use unique dummy wait ident } msleep(@csw, @devmtx, PRIBIO, 'devdrn', hz div 10); end; dev_unlock(); if ((cdp^.cdp_flags and CDP_UNREF_DTR)=0) then begin { avoid out of order notify events } notify_destroy(dev); end; mtx_lock(cdevpriv_mtx); p:=LIST_FIRST(@cdp^.cdp_fdpriv); while (p<>nil) do begin devfs_destroy_cdevpriv(p); mtx_lock(cdevpriv_mtx); // p:=LIST_FIRST(@cdp^.cdp_fdpriv); end; mtx_unlock(cdevpriv_mtx); dev_lock(); dev^.si_drv1:=nil; dev^.si_drv2:=nil; FillChar(dev^.__si_namebuf, sizeof(dev^.__si_namebuf),0); if ((dev^.si_flags and SI_ALIAS)=0) then begin { Remove from cdevsw list } LIST_REMOVE(dev,@dev^.si_list); { If cdevsw has no more struct cdev *'s, clean it } if LIST_EMPTY(@csw^.d_devs) then begin fini_cdevsw(csw); wakeup(@csw^.d_devs); end; end; dev^.si_flags :=dev^.si_flags and (not SI_ALIAS); cdp^.cdp_flags:=cdp^.cdp_flags and (not CDP_UNREF_DTR); Dec(dev^.si_refcount); if (dev^.si_refcount > 0) then LIST_INSERT_HEAD(@dead_cdevsw.d_devs,dev,@dev^.si_list) else dev_free_devlocked(dev); end; procedure delist_dev_locked(dev:p_cdev); var cdp:p_cdev_priv; child:p_cdev; begin mtx_assert(devmtx); cdp:=cdev2priv(dev); if ((cdp^.cdp_flags and CDP_UNREF_DTR)<>0) then Exit; cdp^.cdp_flags:=cdp^.cdp_flags or CDP_UNREF_DTR; dev_refl(dev); devfs_destroy(dev); child:=LIST_FIRST(@dev^.si_children); while (child<>nil) do begin delist_dev_locked(child); child:=LIST_NEXT(child,@child^.si_siblings); end; dev_unlock(); { ensure the destroy event is queued in order } notify_destroy(dev); dev_lock(); end; { * This function will delist a character device and its children from * the directory listing and create a destroy event without waiting * for all character device references to go away. At some later point * destroy_dev() must be called to complete the character device * destruction. After calling this function the character device name * can instantly be re-used. } procedure delist_dev(dev:p_cdev); begin dev_lock(); delist_dev_locked(dev); dev_unlock(); end; procedure destroy_dev(dev:p_cdev); begin dev_lock(); destroy_devl(dev); dev_unlock_and_free(); end; end.