Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
03f4fafe
Commit
03f4fafe
authored
Oct 25, 2004
by
Chas Williams
Committed by
David S. Miller
Oct 25, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[ATM]: [horizon] eliminate pci_find_device()
Signed-off-by:
David S. Miller
<
davem@davemloft.net
>
parent
92b890d8
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
156 additions
and
176 deletions
+156
-176
drivers/atm/horizon.c
drivers/atm/horizon.c
+155
-175
drivers/atm/horizon.h
drivers/atm/horizon.h
+1
-1
No files found.
drivers/atm/horizon.c
View file @
03f4fafe
...
@@ -354,8 +354,7 @@ static inline void __init show_version (void) {
...
@@ -354,8 +354,7 @@ static inline void __init show_version (void) {
/********** globals **********/
/********** globals **********/
static
hrz_dev
*
hrz_devs
=
NULL
;
static
void
do_housekeeping
(
unsigned
long
arg
);
static
struct
timer_list
housekeeping
;
static
unsigned
short
debug
=
0
;
static
unsigned
short
debug
=
0
;
static
unsigned
short
vpi_bits
=
0
;
static
unsigned
short
vpi_bits
=
0
;
...
@@ -1386,7 +1385,7 @@ static inline void rx_data_av_handler (hrz_dev * dev) {
...
@@ -1386,7 +1385,7 @@ static inline void rx_data_av_handler (hrz_dev * dev) {
static
irqreturn_t
interrupt_handler
(
int
irq
,
void
*
dev_id
,
static
irqreturn_t
interrupt_handler
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
pt_regs
)
{
struct
pt_regs
*
pt_regs
)
{
hrz_dev
*
dev
=
hrz_devs
;
hrz_dev
*
dev
=
(
hrz_dev
*
)
dev_id
;
u32
int_source
;
u32
int_source
;
unsigned
int
irq_ok
;
unsigned
int
irq_ok
;
(
void
)
pt_regs
;
(
void
)
pt_regs
;
...
@@ -1397,16 +1396,6 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id,
...
@@ -1397,16 +1396,6 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id,
PRINTD
(
DBG_IRQ
|
DBG_ERR
,
"irq with NULL dev_id: %d"
,
irq
);
PRINTD
(
DBG_IRQ
|
DBG_ERR
,
"irq with NULL dev_id: %d"
,
irq
);
return
IRQ_NONE
;
return
IRQ_NONE
;
}
}
// Did one of our cards generate the interrupt?
while
(
dev
)
{
if
(
dev
==
dev_id
)
break
;
dev
=
dev
->
prev
;
}
if
(
!
dev
)
{
PRINTD
(
DBG_IRQ
,
"irq not for me: %d"
,
irq
);
return
IRQ_NONE
;
}
if
(
irq
!=
dev
->
irq
)
{
if
(
irq
!=
dev
->
irq
)
{
PRINTD
(
DBG_IRQ
|
DBG_ERR
,
"irq mismatch: %d"
,
irq
);
PRINTD
(
DBG_IRQ
|
DBG_ERR
,
"irq mismatch: %d"
,
irq
);
return
IRQ_NONE
;
return
IRQ_NONE
;
...
@@ -1462,28 +1451,18 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id,
...
@@ -1462,28 +1451,18 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id,
/********** housekeeping **********/
/********** housekeeping **********/
static
void
set_timer
(
struct
timer_list
*
timer
,
unsigned
int
delay
)
{
timer
->
expires
=
jiffies
+
delay
;
add_timer
(
timer
);
return
;
}
static
void
do_housekeeping
(
unsigned
long
arg
)
{
static
void
do_housekeeping
(
unsigned
long
arg
)
{
// just stats at the moment
// just stats at the moment
hrz_dev
*
dev
=
hrz_devs
;
hrz_dev
*
dev
=
(
hrz_dev
*
)
arg
;
(
void
)
arg
;
// data is set to zero at module unload
// collect device-specific (not driver/atm-linux) stats here
if
(
housekeeping
.
data
)
{
dev
->
tx_cell_count
+=
rd_regw
(
dev
,
TX_CELL_COUNT_OFF
);
while
(
dev
)
{
dev
->
rx_cell_count
+=
rd_regw
(
dev
,
RX_CELL_COUNT_OFF
);
// collect device-specific (not driver/atm-linux) stats here
dev
->
hec_error_count
+=
rd_regw
(
dev
,
HEC_ERROR_COUNT_OFF
);
dev
->
tx_cell_count
+=
rd_regw
(
dev
,
TX_CELL_COUNT_OFF
);
dev
->
unassigned_cell_count
+=
rd_regw
(
dev
,
UNASSIGNED_CELL_COUNT_OFF
);
dev
->
rx_cell_count
+=
rd_regw
(
dev
,
RX_CELL_COUNT_OFF
);
dev
->
hec_error_count
+=
rd_regw
(
dev
,
HEC_ERROR_COUNT_OFF
);
mod_timer
(
&
dev
->
housekeeping
,
jiffies
+
HZ
/
10
);
dev
->
unassigned_cell_count
+=
rd_regw
(
dev
,
UNASSIGNED_CELL_COUNT_OFF
);
dev
=
dev
->
prev
;
}
set_timer
(
&
housekeeping
,
HZ
/
10
);
}
return
;
return
;
}
}
...
@@ -2719,157 +2698,176 @@ static const struct atmdev_ops hrz_ops = {
...
@@ -2719,157 +2698,176 @@ static const struct atmdev_ops hrz_ops = {
.
owner
=
THIS_MODULE
,
.
owner
=
THIS_MODULE
,
};
};
static
int
__init
hrz_probe
(
void
)
{
static
int
__devinit
hrz_probe
(
struct
pci_dev
*
pci_dev
,
const
struct
pci_device_id
*
pci_ent
)
struct
pci_dev
*
pci_dev
;
{
int
devs
;
hrz_dev
*
dev
;
int
err
=
0
;
PRINTD
(
DBG_FLOW
,
"hrz_probe"
);
devs
=
0
;
pci_dev
=
NULL
;
while
((
pci_dev
=
pci_find_device
(
PCI_VENDOR_ID_MADGE
,
PCI_DEVICE_ID_MADGE_HORIZON
,
pci_dev
)
))
{
hrz_dev
*
dev
;
// adapter slot free, read resources from PCI configuration space
u32
iobase
=
pci_resource_start
(
pci_dev
,
0
);
u32
*
membase
=
bus_to_virt
(
pci_resource_start
(
pci_dev
,
1
));
u8
irq
=
pci_dev
->
irq
;
/* XXX DEV_LABEL is a guess */
if
(
!
request_region
(
iobase
,
HRZ_IO_EXTENT
,
DEV_LABEL
))
continue
;
if
(
pci_enable_device
(
pci_dev
))
// adapter slot free, read resources from PCI configuration space
continue
;
u32
iobase
=
pci_resource_start
(
pci_dev
,
0
);
u32
*
membase
=
bus_to_virt
(
pci_resource_start
(
pci_dev
,
1
));
dev
=
kmalloc
(
sizeof
(
hrz_dev
),
GFP_KERNEL
);
u8
irq
=
pci_dev
->
irq
;
if
(
!
dev
)
{
// perhaps we should be nice: deregister all adapters and abort?
PRINTD
(
DBG_ERR
,
"out of memory"
);
continue
;
}
memset
(
dev
,
0
,
sizeof
(
hrz_dev
));
// grab IRQ and install handler - move this someplace more sensible
if
(
request_irq
(
irq
,
interrupt_handler
,
SA_SHIRQ
,
/* irqflags guess */
DEV_LABEL
,
/* name guess */
dev
))
{
PRINTD
(
DBG_WARN
,
"request IRQ failed!"
);
// free_irq is at "endif"
}
else
{
PRINTD
(
DBG_INFO
,
"found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p"
,
iobase
,
irq
,
membase
);
dev
->
atm_dev
=
atm_dev_register
(
DEV_LABEL
,
&
hrz_ops
,
-
1
,
NULL
);
if
(
!
(
dev
->
atm_dev
))
{
PRINTD
(
DBG_ERR
,
"failed to register Madge ATM adapter"
);
}
else
{
unsigned
char
lat
;
unsigned
char
lat
;
PRINTD
(
DBG_INFO
,
"registered Madge ATM adapter (no. %d) (%p) at %p"
,
PRINTD
(
DBG_FLOW
,
"hrz_probe"
);
dev
->
atm_dev
->
number
,
dev
,
dev
->
atm_dev
);
/* XXX DEV_LABEL is a guess */
if
(
!
request_region
(
iobase
,
HRZ_IO_EXTENT
,
DEV_LABEL
))
return
-
EINVAL
;
if
(
pci_enable_device
(
pci_dev
))
{
err
=
-
EINVAL
;
goto
out_release
;
}
dev
=
kmalloc
(
sizeof
(
hrz_dev
),
GFP_KERNEL
);
if
(
!
dev
)
{
// perhaps we should be nice: deregister all adapters and abort?
PRINTD
(
DBG_ERR
,
"out of memory"
);
err
=
-
ENOMEM
;
goto
out_disable
;
}
memset
(
dev
,
0
,
sizeof
(
hrz_dev
));
pci_set_drvdata
(
pci_dev
,
dev
);
// grab IRQ and install handler - move this someplace more sensible
if
(
request_irq
(
irq
,
interrupt_handler
,
SA_SHIRQ
,
/* irqflags guess */
DEV_LABEL
,
/* name guess */
dev
))
{
PRINTD
(
DBG_WARN
,
"request IRQ failed!"
);
err
=
-
EINVAL
;
goto
out_free
;
}
PRINTD
(
DBG_INFO
,
"found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p"
,
iobase
,
irq
,
membase
);
dev
->
atm_dev
=
atm_dev_register
(
DEV_LABEL
,
&
hrz_ops
,
-
1
,
NULL
);
if
(
!
(
dev
->
atm_dev
))
{
PRINTD
(
DBG_ERR
,
"failed to register Madge ATM adapter"
);
err
=
-
EINVAL
;
goto
out_free_irq
;
}
PRINTD
(
DBG_INFO
,
"registered Madge ATM adapter (no. %d) (%p) at %p"
,
dev
->
atm_dev
->
number
,
dev
,
dev
->
atm_dev
);
dev
->
atm_dev
->
dev_data
=
(
void
*
)
dev
;
dev
->
atm_dev
->
dev_data
=
(
void
*
)
dev
;
dev
->
pci_dev
=
pci_dev
;
dev
->
pci_dev
=
pci_dev
;
// enable bus master accesses
// enable bus master accesses
pci_set_master
(
pci_dev
);
pci_set_master
(
pci_dev
);
// frobnicate latency (upwards, usually)
// frobnicate latency (upwards, usually)
pci_read_config_byte
(
pci_dev
,
PCI_LATENCY_TIMER
,
&
lat
);
pci_read_config_byte
(
pci_dev
,
PCI_LATENCY_TIMER
,
&
lat
);
if
(
pci_lat
)
{
if
(
pci_lat
)
{
PRINTD
(
DBG_INFO
,
"%s PCI latency timer from %hu to %hu"
,
PRINTD
(
DBG_INFO
,
"%s PCI latency timer from %hu to %hu"
,
"changing"
,
lat
,
pci_lat
);
"changing"
,
lat
,
pci_lat
);
pci_write_config_byte
(
pci_dev
,
PCI_LATENCY_TIMER
,
pci_lat
);
pci_write_config_byte
(
pci_dev
,
PCI_LATENCY_TIMER
,
pci_lat
);
}
else
if
(
lat
<
MIN_PCI_LATENCY
)
{
}
else
if
(
lat
<
MIN_PCI_LATENCY
)
{
PRINTK
(
KERN_INFO
,
"%s PCI latency timer from %hu to %hu"
,
PRINTK
(
KERN_INFO
,
"%s PCI latency timer from %hu to %hu"
,
"increasing"
,
lat
,
MIN_PCI_LATENCY
);
"increasing"
,
lat
,
MIN_PCI_LATENCY
);
pci_write_config_byte
(
pci_dev
,
PCI_LATENCY_TIMER
,
MIN_PCI_LATENCY
);
pci_write_config_byte
(
pci_dev
,
PCI_LATENCY_TIMER
,
MIN_PCI_LATENCY
);
}
}
dev
->
iobase
=
iobase
;
dev
->
iobase
=
iobase
;
dev
->
irq
=
irq
;
dev
->
irq
=
irq
;
dev
->
membase
=
membase
;
dev
->
membase
=
membase
;
dev
->
rx_q_entry
=
dev
->
rx_q_reset
=
&
memmap
->
rx_q_entries
[
0
];
dev
->
rx_q_entry
=
dev
->
rx_q_reset
=
&
memmap
->
rx_q_entries
[
0
];
dev
->
rx_q_wrap
=
&
memmap
->
rx_q_entries
[
RX_CHANS
-
1
];
dev
->
rx_q_wrap
=
&
memmap
->
rx_q_entries
[
RX_CHANS
-
1
];
// these next three are performance hacks
// these next three are performance hacks
dev
->
last_vc
=
-
1
;
dev
->
last_vc
=
-
1
;
dev
->
tx_last
=
-
1
;
dev
->
tx_last
=
-
1
;
dev
->
tx_idle
=
0
;
dev
->
tx_idle
=
0
;
dev
->
tx_regions
=
0
;
dev
->
tx_regions
=
0
;
dev
->
tx_bytes
=
0
;
dev
->
tx_bytes
=
0
;
dev
->
tx_skb
=
NULL
;
dev
->
tx_skb
=
NULL
;
dev
->
tx_iovec
=
NULL
;
dev
->
tx_iovec
=
NULL
;
dev
->
tx_cell_count
=
0
;
dev
->
tx_cell_count
=
0
;
dev
->
rx_cell_count
=
0
;
dev
->
rx_cell_count
=
0
;
dev
->
hec_error_count
=
0
;
dev
->
hec_error_count
=
0
;
dev
->
unassigned_cell_count
=
0
;
dev
->
unassigned_cell_count
=
0
;
dev
->
noof_spare_buffers
=
0
;
dev
->
noof_spare_buffers
=
0
;
{
{
unsigned
int
i
;
unsigned
int
i
;
for
(
i
=
0
;
i
<
TX_CHANS
;
++
i
)
for
(
i
=
0
;
i
<
TX_CHANS
;
++
i
)
dev
->
tx_channel_record
[
i
]
=
-
1
;
dev
->
tx_channel_record
[
i
]
=
-
1
;
}
}
dev
->
flags
=
0
;
dev
->
flags
=
0
;
// Allocate cell rates and remember ASIC version
// Allocate cell rates and remember ASIC version
// Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
// Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
// Copper: (WRONG) we want 6 into the above, close to 25Mb/s
// Copper: (WRONG) we want 6 into the above, close to 25Mb/s
// Copper: (plagarise!) 25600000/8/270*260/53 - n/53
// Copper: (plagarise!) 25600000/8/270*260/53 - n/53
if
(
hrz_init
(
dev
))
{
if
(
hrz_init
(
dev
))
{
// to be really pedantic, this should be ATM_OC3c_PCR
// to be really pedantic, this should be ATM_OC3c_PCR
dev
->
tx_avail
=
ATM_OC3_PCR
;
dev
->
tx_avail
=
ATM_OC3_PCR
;
dev
->
rx_avail
=
ATM_OC3_PCR
;
dev
->
rx_avail
=
ATM_OC3_PCR
;
set_bit
(
ultra
,
&
dev
->
flags
);
// NOT "|= ultra" !
set_bit
(
ultra
,
&
dev
->
flags
);
// NOT "|= ultra" !
}
else
{
}
else
{
dev
->
tx_avail
=
((
25600000
/
8
)
*
26
)
/
(
27
*
53
);
dev
->
tx_avail
=
((
25600000
/
8
)
*
26
)
/
(
27
*
53
);
dev
->
rx_avail
=
((
25600000
/
8
)
*
26
)
/
(
27
*
53
);
dev
->
rx_avail
=
((
25600000
/
8
)
*
26
)
/
(
27
*
53
);
PRINTD
(
DBG_WARN
,
"Buggy ASIC: no TX bus-mastering."
);
PRINTD
(
DBG_WARN
,
"Buggy ASIC: no TX bus-mastering."
);
}
}
// rate changes spinlock
// rate changes spinlock
spin_lock_init
(
&
dev
->
rate_lock
);
spin_lock_init
(
&
dev
->
rate_lock
);
// on-board memory access spinlock; we want atomic reads and
// on-board memory access spinlock; we want atomic reads and
// writes to adapter memory (handles IRQ and SMP)
// writes to adapter memory (handles IRQ and SMP)
spin_lock_init
(
&
dev
->
mem_lock
);
spin_lock_init
(
&
dev
->
mem_lock
);
init_waitqueue_head
(
&
dev
->
tx_queue
);
init_waitqueue_head
(
&
dev
->
tx_queue
);
// vpi in 0..4, vci in 6..10
// vpi in 0..4, vci in 6..10
dev
->
atm_dev
->
ci_range
.
vpi_bits
=
vpi_bits
;
dev
->
atm_dev
->
ci_range
.
vpi_bits
=
vpi_bits
;
dev
->
atm_dev
->
ci_range
.
vci_bits
=
10
-
vpi_bits
;
dev
->
atm_dev
->
ci_range
.
vci_bits
=
10
-
vpi_bits
;
// update count and linked list
init_timer
(
&
dev
->
housekeeping
);
++
devs
;
dev
->
housekeeping
.
function
=
do_housekeeping
;
dev
->
prev
=
hrz_devs
;
dev
->
housekeeping
.
data
=
(
unsigned
long
)
dev
;
hrz_devs
=
dev
;
mod_timer
(
&
dev
->
housekeeping
,
jiffies
);
// success
continue
;
out:
return
err
;
/* not currently reached */
atm_dev_deregister
(
dev
->
atm_dev
);
out_free_irq:
}
/* atm_dev_register */
free_irq
(
dev
->
irq
,
dev
);
free_irq
(
irq
,
dev
);
out_free:
kfree
(
dev
);
}
/* request_irq */
out_disable:
kfree
(
dev
);
pci_disable_device
(
pci_dev
);
release_region
(
iobase
,
HRZ_IO_EXTENT
);
out_release:
}
/* kmalloc and while */
release_region
(
iobase
,
HRZ_IO_EXTENT
);
return
devs
;
goto
out
;
}
static
void
__devexit
hrz_remove_one
(
struct
pci_dev
*
pci_dev
)
{
hrz_dev
*
dev
;
dev
=
pci_get_drvdata
(
pci_dev
);
PRINTD
(
DBG_INFO
,
"closing %p (atm_dev = %p)"
,
dev
,
dev
->
atm_dev
);
del_timer_sync
(
&
dev
->
housekeeping
);
hrz_reset
(
dev
);
atm_dev_deregister
(
dev
->
atm_dev
);
free_irq
(
dev
->
irq
,
dev
);
release_region
(
dev
->
iobase
,
HRZ_IO_EXTENT
);
kfree
(
dev
);
pci_disable_device
(
pci_dev
);
}
}
static
void
__init
hrz_check_args
(
void
)
{
static
void
__init
hrz_check_args
(
void
)
{
...
@@ -2909,11 +2907,22 @@ MODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames");
...
@@ -2909,11 +2907,22 @@ MODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames");
MODULE_PARM_DESC
(
max_rx_size
,
"maximum size of RX AAL5 frames"
);
MODULE_PARM_DESC
(
max_rx_size
,
"maximum size of RX AAL5 frames"
);
MODULE_PARM_DESC
(
pci_lat
,
"PCI latency in bus cycles"
);
MODULE_PARM_DESC
(
pci_lat
,
"PCI latency in bus cycles"
);
static
struct
pci_device_id
hrz_pci_tbl
[]
=
{
{
PCI_VENDOR_ID_MADGE
,
PCI_DEVICE_ID_MADGE_HORIZON
,
PCI_ANY_ID
,
PCI_ANY_ID
,
0
,
0
,
0
},
{
0
,
}
};
static
struct
pci_driver
hrz_driver
=
{
.
name
=
"horizon"
,
.
probe
=
hrz_probe
,
.
remove
=
__devexit_p
(
hrz_remove_one
),
.
id_table
=
hrz_pci_tbl
,
};
/********** module entry **********/
/********** module entry **********/
static
int
__init
hrz_module_init
(
void
)
{
static
int
__init
hrz_module_init
(
void
)
{
int
devs
;
// sanity check - cast is needed since printk does not support %Zu
// sanity check - cast is needed since printk does not support %Zu
if
(
sizeof
(
struct
MEMMAP
)
!=
128
*
1024
/
4
)
{
if
(
sizeof
(
struct
MEMMAP
)
!=
128
*
1024
/
4
)
{
PRINTK
(
KERN_ERR
,
"Fix struct MEMMAP (is %lu fakewords)."
,
PRINTK
(
KERN_ERR
,
"Fix struct MEMMAP (is %lu fakewords)."
,
...
@@ -2927,44 +2936,15 @@ static int __init hrz_module_init (void) {
...
@@ -2927,44 +2936,15 @@ static int __init hrz_module_init (void) {
hrz_check_args
();
hrz_check_args
();
// get the juice
// get the juice
devs
=
hrz_probe
();
return
pci_module_init
(
&
hrz_driver
);
if
(
devs
)
{
init_timer
(
&
housekeeping
);
housekeeping
.
function
=
do_housekeeping
;
// paranoia
housekeeping
.
data
=
1
;
set_timer
(
&
housekeeping
,
0
);
}
else
{
PRINTK
(
KERN_ERR
,
"no (usable) adapters found"
);
}
return
devs
?
0
:
-
ENODEV
;
}
}
/********** module exit **********/
/********** module exit **********/
static
void
__exit
hrz_module_exit
(
void
)
{
static
void
__exit
hrz_module_exit
(
void
)
{
hrz_dev
*
dev
;
PRINTD
(
DBG_FLOW
,
"cleanup_module"
);
PRINTD
(
DBG_FLOW
,
"cleanup_module"
);
// paranoia
return
pci_unregister_driver
(
&
hrz_driver
);
housekeeping
.
data
=
0
;
del_timer
(
&
housekeeping
);
while
(
hrz_devs
)
{
dev
=
hrz_devs
;
hrz_devs
=
dev
->
prev
;
PRINTD
(
DBG_INFO
,
"closing %p (atm_dev = %p)"
,
dev
,
dev
->
atm_dev
);
hrz_reset
(
dev
);
atm_dev_deregister
(
dev
->
atm_dev
);
free_irq
(
dev
->
irq
,
dev
);
release_region
(
dev
->
iobase
,
HRZ_IO_EXTENT
);
kfree
(
dev
);
}
return
;
}
}
module_init
(
hrz_module_init
);
module_init
(
hrz_module_init
);
...
...
drivers/atm/horizon.h
View file @
03f4fafe
...
@@ -457,7 +457,7 @@ struct hrz_dev {
...
@@ -457,7 +457,7 @@ struct hrz_dev {
unsigned
long
unassigned_cell_count
;
unsigned
long
unassigned_cell_count
;
struct
pci_dev
*
pci_dev
;
struct
pci_dev
*
pci_dev
;
struct
hrz_dev
*
prev
;
struct
timer_list
housekeeping
;
};
};
typedef
struct
hrz_dev
hrz_dev
;
typedef
struct
hrz_dev
hrz_dev
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment