text:
This chapter describes the operation of STREAMS drivers and some of the processing typically required in the drivers.
STREAMS drivers can be considered a subset of device drivers in general and character device drivers in particular. While there are some differences between STREAMS drivers and non-STREAMS drivers, much of the information contained in Writing Device Drivers also applies to STREAMS drivers.
The word module is used differently when talking about drivers. A device driver is a kernel-loadable module that provides the interface between a device and the Device Driver Interface, and is linked to the kernel when it is first invoked.
STREAMS drivers share a basic programming model with STREAMS modules. Information common to both drivers and modules is discussed in Chapter 10, STREAMS Modules. After summarizing some basic device driver concepts, this chapter discusses several topics specific to STREAMS device drivers (and not covered elsewhere) and then presents code samples illustrating basic STREAMS driver processing.
A device driver is a loadable kernel module that translates between an I/O device and the kernel to operate the device.
Device drivers can also be software-only, implementing a pseudo-device such as RAM disk or a pseudo-terminal that only exists in software.
In the Solaris operating environment, the interface between the kernel and device drivers is called the Device Driver Interface (DDI/DKI). This interface is specified in the Section 9E manual pages that specify the driver entry points. Section 9 also details the kernel data structures (9S) and utility functions (9F) available to drivers.
The DDI protects the kernel from device specifics. Application programs and the rest of the kernel need little (if any) device-specific code to use the device. The DDI makes the system more portable and easier to maintain.
There are three basic types of device drivers corresponding to the three basic types of devices. Character devices handle data serially and transfer data to and from the processor one character at a time, the same as keyboards and low performance printers. Serial block devices and drivers also handle data serially, but transfer data to and from memory without processor intervention, the same as tape drives. Direct access block devices and drivers also transfer data without processor intervention and blocks of storage on the device can be addressed directly, the same as disk drives.
There are two types of character device drivers: standard character device drivers and STREAMS device drivers. STREAMS is a separate programming model for writing a character driver. Devices that receive data asynchronously (such as terminal and network devices) are suited to a STREAMS implementation.
STREAMS drivers share some kinds of processing with STREAMS modules. Important differences between drivers and modules include how the application manipulates drivers and modules and how interrupts are handled. In STREAMS, drivers are opened and modules are pushed. A device driver has an interrupt routine to process hardware interrupts.
STREAMS drivers have five different points of contact with the kernel:Table 9–1 Kernel Contact Points
Kernel Contact Point
Configuration entry points
These kernel (dynamically loading) routines enable the kernel to find the driver binary in the file system and load it into, or unload it from, the running kernel. The entry points include _init(9E), _info(9E), and _fini(9E).
Initialization entry points
Enable the driver to determine a device's presence and initialize its state. These routines are accessed through the dev_ops(9S) data structure during system initialization. They include getinfo(9E), probe(9E), attach(9E), and detach(9E).
Table-driven entry points
Accessed through cb_ops(9S), the character and block access table, when an application calls the appropriate interface. The members of the cb_ops(9S) structure include pointers to entry points that perform the device's functions, such as read(9E), write(9E), ioctl(9E). The cb_ops(9S) table contains a pointer to the streamtab(9S)structure.
STREAMS queue processing entry points
Contained in the streamtab, read and process the STREAMS messages that travel through the queue structures. Examples of STREAMS queue processing entry points are put(9E) and srv(9E).
Interrupt routines
Handle the interrupts from the device (or software interrupts). It is added to the kernel by ddi_add_intr(9F) when the kernel configuration software calls attach(9E).
As with other SunOS 5 drivers, STREAMS drivers are dynamically linked and loaded when referred to for the first time. For example, when the system is initially booted, the STREAMS pseudo-tty slave pseudo-driver (pts(7D)) is loaded automatically into the kernel when it is first opened.
In STREAMS, the header declarations differ between drivers and modules. The word "module" is used in two different ways when talking about drivers. There are STREAMS modules, which are pushable nondriver entities, and there are kernel-loadable modules, which are components of the kernel. See the appropriate chapters in Writing Device Drivers.
The kernel configuration mechanism must distinguish between STREAMS devices and traditional character devices because system calls to STREAMS drivers are processed by STREAMS routines, not by the system driver routines. The streamtab pointer in the cb_ops(9S) structure provides this distinction. If it is NULL, there are no STREAMS routines to execute; otherwise, STREAMS drivers initialize streamtab with a pointer to a streamtab(9S) structure containing the driver's STREAMS queue processing entry points.
The initialization entry points of STREAMS drivers must perform the same tasks as those of non-STREAMS drivers. See Writing Device Drivers for more information.
In non-STREAMS drivers, most of the driver's work is accomplished through the entry points in the cb_ops(9S) structure. For STREAMS drivers, most of the work is accomplished through the message-based STREAMS queue processing entry points.
Figure 9–1 shows multiple streams (corresponding to minor devices) connecting to a common driver. There are two distinct streams opened from the same major device. Consequently, they have the same streamtab and the same driver procedures.
Multiple instances (minor devices) of the same driver are handled during the initial open for each device. Typically, a driver stores the queue address in a driver-private structure that is uniquely identified by the minor device number. (The DDI/DKI provides a mechanism for uniform handling of driver-private structures; see ddi_soft_state(9F)). The q_ptr of the queue points to the private data structure entry. When the messages are received by the queue, the calls to the driver put and service procedures pass the address of the queue, enabling the procedures to determine the associated device through the q_ptr field.
STREAMS guarantees that only one open or close can be active at a time per major/minor device pair.
STREAMS device drivers have processing routines that are registered with the framework through the streamtab structure. The put procedure is a driver's entry point, but it is a message (not system) interface. STREAMS drivers and STREAMS modules implement these entry points similarly, as described in Entry Points.
The stream head translates write(2) and ioctl(2) calls into messages and sends them downstream to be processed by the driver's write queue put(9E) procedure. read is seen directly only by the stream head, which contains the functions required to process system calls. A STREAMS driver does not check system interfaces other than open and close, but it can detect the absence of a read indirectly if flow control propagates from the stream head to the driver and affects the driver's ability to send messages upstream.
For read-side processing, when the driver is ready to send data or other information to a user process, it prepares a message and sends it upstream to the read queue of the appropriate (minor device) stream. The driver's open routine generally stores the queue address corresponding to this stream.
For write-side (or output) processing, the driver receives messages in place of a write call. If the message cannot be sent immediately to the hardware, it may be stored on the driver's write message queue. Subsequent output interrupts can remove messages from this queue.
A driver is at the end of a stream. As a result, drivers must include standard processing for certain message types that a module might be able to pass to the next component. For example, a driver must process all M_IOCTL messages; otherwise, the stream head blocks for an M_IOCNAK, M_IOCACK, or until the timeout (potentially infinite) expires. If a driver does not understand an ioctl(2), an M_IOCNAK message is sent upstream.
Messages that are not understood by the drivers should be freed.
The stream head locks up the stream when it receives an M_ERROR message, so driver developers should be careful when using the M_ERROR message.
Most hardware drivers have an interrupt handler routine. You must supply an interrupt routine for the device's driver. The interrupt handling for STREAMS drivers is not fundamentally different from that for other device drivers. Drivers usually register interrupt handlers in their attach(9E)entry point, using ddi_add_intr(9F). Drivers unregister the interrupt handler at detach time using ddi_remove_intr(9F).
The system also supports software interrupts. The routines ddi_add_softintr(9F) and ddi_remove_softintr(9F) register and unregister (respectively) soft-interrupt handlers. A software interrupt is generated by calling ddi_trigger_softintr(9F).
See Writing Device Drivers for more information.
STREAMS drivers can prevent unloading through the standard driver detach(9E) entry point.
The following discussion describes characteristics of a STREAMS driver:
Basic hardware/pseudo drivers
This type of driver communicates with a specific piece of hardware (or simulated hardware). The lp example simulates a simple printer driver.
Clonable drivers
The STREAMS framework supports a CLONEOPEN facility, which allows multiple streams to be opened from a single special file. If a STREAMS device driver chooses to support CLONEOPEN, it can be referred to as a clonable device. The attach(9E) routines from two Solaris drivers, ptm(7D) and log(7D), illustrate two approaches to cloning.
Multiple instances in drivers
A multiplexer driver is a regular STREAMS driver that can handle multiple streams connected to it instead of just one stream. Multiple connections occur when more than one minor device of the same driver is in use. See Cloning STREAMS Drivers for more information.
Example 9–1 is a sample print driver for an interrupt-per-character line printer. The driver is unidirectional—it has no read-side processing. It demonstrates some differences between module and driver programming, including the following:
Declarations for driver configuration
Open handling
A driver is passed a device number
Flush handling
A driver must loop M_FLUSH messages back upstream
Interrupt routine
A driver registers interrupt handler and processes interrupts
Most of the STREAMS processing in the driver is independent of the actual printer hardware; in this example, actual interaction with the printer is limited to the lpoutchar function, which prints one character at a time. For purposes of demonstration, the "printer hardware" is actually the system console, accessed through cmn_err(9F). Since there's no actual hardware to generate a genuine hardware interrupt, lpoutchar simulates interrupts using ddi_trigger_softintr(9F). For a real printer, the lpoutchar function is rewritten to send a character to the printer, which should generate a hardware interrupt.
The driver declarations follow. After specifying header files (include <sys/ddi.h> and <sys/sunddi.h> as the last two header files), the driver declares a per-printer structure, struct lp. This structure contains members that enable the driver to keep track of each instance of the driver, such as flags (what the driver is doing), msg (the current STREAMS print message), qptr (pointer to the stream's write queue), dip (the instance's device information handle), iblock_cookie (for registering an interrupt handler), siid (the handle of the soft interrupt), and lp_lock (a mutex to protect the data structure from multithreaded race conditions). The driver next defines the bits for the flags member of struct lp; the driver defines only one flag, BUSY.
Following function prototypes, the driver provides some standard STREAMS declarations: a module_info(9S) structure (minfo), a qinit(9S) structure for the read side (rinit) that is initialized by the driver's open and close entry points, a qinit(9S) structure for the write side (winit) that is initialized by the write put procedure, and a streamtab(9S) that points to rinit and winit. The values in the module name and ID fields in the module_info(9S) structure must be unique in the system. Because the driver is unidirectional, there is no read side put or service procedure. The flow control limits for use on the write side are 50 bytes for the low-watermark and 150 bytes for the high-watermark.
The driver next declares lp_state. This is an anchor on which the various "soft-state" functions provided by the DDK operate. The ddi_soft_state(9F) manual page describes how to maintain multiple instances of a driver.
The driver next declares acb_ops(9S) structure, which is required in all device drivers. In non-STREAMS device drivers, cb_ops(9S) contains vectors to the table-driven entry points. For STREAMS drivers, however, cb_ops(9S) contains mostly nodev entries. The cb_stream field, however, is initialized with a pointer to the driver's streamtab(9S) structure. This indicates to the kernel that this driver is a STREAMS driver.
Next, the driver declares a dev_ops(9S) structure, which points to the various initialization entry points as well as to the cb_ops(9S) structure. Finally, the driver declares a struct moldrv and a struct modlinkage for use by the kernel linker when the driver is dynamically loaded. struct moldrv contains a pointer to mod_driverops (a significant difference between a STREAMS driver and a STREAMS module—a STREAMS module would contain a pointer to mod_strops instead).
#include <sys/types.h> #include <sys/param.h> #include <sys/stream.h> #include <sys/stropts.h> #include <sys/signal.h> #include <sys/errno.h> #include <sys/cred.h> #include <sys/stat.h> #include <sys/modctl.h> #include <sys/conf.h> #include <sys/ddi.h> #include <sys/sunddi.h> /* This is a private data structure, one per minor device number */ struct lp { short flags; /* flags -- see below */ mblk_t *msg; /* current message being output */ queue_t *qptr; /* back pointer to write queue */ dev_info_t *dip; /* devinfo handle */ ddi_iblock_cookie_t iblock_cookie; ddi_softintr_t siid; kmutex_t lp_lock; /* sync lock */ }; /* flags bits */ #define BUSY 1 /* dev is running, int is forthcoming */ /* * Function prototypes. */ static int lpattach(dev_info_t *, ddi_attach_cmd_t); static int lpdetach(dev_info_t *, ddi_detach_cmd_t); static int lpgetinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); static int lpidentify(dev_info_t *); static uint lpintr(caddr_t lp); static void lpout(struct lp *lp); static void lpoutchar(struct lp *lp, char c); static int lpopen(queue_t*, dev_t*, int, int, cred_t*); static int lpclose(queue_t*, int, cred_t*); static int lpwput(queue_t*, mblk_t*); /* Standard Streams declarations */ static struct module_info minfo = { 0xaabb, "lp", 0, INFPSZ, 150, 50 }; static struct qinit rinit = { (int (*)()) NULL, (int (*)()) NULL, lpopen, lpclose, (int (*)()) NULL, &minfo, NULL }; static struct qinit winit = { lpwput, (int (*)()) NULL, (int (*)()) NULL, (int (*)()) NULL, (int (*)()) NULL, &minfo, NULL }; static struct streamtab lpstrinfo = { &rinit, &winit, NULL, NULL }; /* * An opaque handle where our lp lives */ static void *lp_state; /* Module Loading/Unloading and Autoconfiguration declarations */ static struct cb_ops lp_cb_ops = { nodev, /* cb_open */ nodev, /* cb_close */ nodev, /* cb_strategy */ nodev, /* cb_print */ nodev, /* cb_dump */ nodev, /* cb_read */ nodev, /* cb_write */ nodev, /* cb_ioctl */ nodev, /* cb_devmap */ nodev, /* cb_mmap */ nodev, /* cb_segmap */ nochpoll, /* cb_chpoll */ ddi_prop_op, /* cb_prop_op */ &lpstrinfo, /* cb_stream */ D_MP | D_NEW, /* cb_flag */ }; static struct dev_ops lp_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ lpgetinfo, /* devo_getinfo */ lpidentify, /* devo_identify */ nulldev, /* devo_probe */ lpattach, /* devo_attach */ lpdetach, /* devo_detach */ nodev, /* devo_reset */ &lp_cb_ops, /* devo_cb_ops */ (struct bus_ops *)NULL /* devo_bus_ops */ }; /* * Module linkage information for the kernel. */ static struct modldrv modldrv = { &mod_driverops, "Simple Sample Printer Streams Driver", /* Description */ &lp_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL };
Example 9–2 shows the required driver configuration entry points _init(9E), _fini(9E), and _info(9E). In addition to installing the driver using mod_install(9F), the _init entry point also initializes the per-instance driver structure using ddi_soft_state_init(9F). _fini(9E) performs the complementary calls to mod_remove(9F) and ddi_soft_state_fini(9F) to unload the driver and release the resources used by the soft-state routines.
int _init(void) { int e; if ((e = ddi_soft_state_init(&lp_state, sizeof (struct lp), 1)) != 0) { return (e); } if ((e = mod_install(&modlinkage)) != 0) { ddi_soft_state_fini(&lp_state); } return (e); } int _fini(void) { int e; if ((e = mod_remove(&modlinkage)) != 0) { return (e); } ddi_soft_state_fini(&lp_state); return (e); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); }
Example 9–3 shows the lp driver's implementation of the initialization entry points. In lpidentify, the driver ensures that the name of the device being attached is "lp".
lpattach first uses ddi_soft_state_zalloc(9F) to allocate a per-instance structure for the printer being attached. Next it creates a node in the device tree for the printer using ddi_create_minor_node(9F); user programs use the node to access the device. lpattach then registers the driver interrupt handler because the sample is driver pseudo-hardware, the driver uses soft interrupts. A driver for a real printer would use ddi_add_intr(9F) instead of ddi_add_softintr(9F). A driver for a real printer would also need to perform any other required hardware initialization in lpattach. Finally, lpattach initializes the per-instance mutex.
In lpdetach, the driver undoes everything it did in lpattach.
lpgetinfo uses the soft-state structures to obtain the required information.
static int lpidentify(dev_info_t *dip) { if (strcmp(ddi_get_name(dip), "lp") == 0) { return (DDI_IDENTIFIED); } else return (DDI_NOT_IDENTIFIED); } static int lpattach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance; struct lp *lpp; switch (cmd) { case DDI_ATTACH: instance = ddi_get_instance(dip); if (ddi_soft_state_zalloc(lp_state, instance) != DDI_SUCCESS) { cmn_err(CE_CONT, "%s%d: can't allocate state\n", ddi_get_name(dip), instance); return (DDI_FAILURE); } else lpp = ddi_get_soft_state(lp_state, instance); if (ddi_create_minor_node(dip, "strlp", S_IFCHR, instance, NULL, 0) == DDI_FAILURE) { ddi_remove_minor_node(dip, NULL); goto attach_failed; } lpp->dip = dip; ddi_set_driver_private(dip, (caddr_t)lpp); /* add (soft) interrupt */ if (ddi_add_softintr(dip, DDI_SOFTINT_LOW, &lpp->siid, &lpp->iblock_cookie, 0, lpintr, (caddr_t)lpp) != DDI_SUCCESS) { ddi_remove_minor_node(dip, NULL); goto attach_failed; } mutex_init(&lpp->lp_lock, "lp lock", MUTEX_DRIVER, (void *)lpp->iblock_cookie); ddi_report_dev(dip); return (DDI_SUCCESS); default: return (DDI_FAILURE); } attach_failed: /* * Use our own detach routine to toss * away any stuff we allocated above. */ (void) lpdetach(dip, DDI_DETACH); return (DDI_FAILURE); } static int lpdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) { int instance; struct lp *lpp; switch (cmd) { case DDI_DETACH: /* * Undo what we did in lpattach, freeing resources * and removing things we installed. The system * framework guarantees we are not active with this devinfo * node in any other entry points at this time. */ ddi_prop_remove_all(dip); instance = ddi_get_instance(dip); lpp = ddi_get_soft_state(lp_state, instance); ddi_remove_minor_node(dip, NULL); ddi_remove_softintr(lpp->siid); ddi_soft_state_free(lp_state, instance); return (DDI_SUCCESS); default: return (DDI_FAILURE); } } /*ARGSUSED*/ static int lpgetinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { struct lp *lpp; int error = DDI_FAILURE; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if ((lpp = ddi_get_soft_state(lp_state, getminor((dev_t)arg))) != NULL) { *result = lpp->dip; error = DDI_SUCCESS; } else *result = NULL; break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)getminor((dev_t)arg); error = DDI_SUCCESS; break; default: break; } return (error); }
The STREAMS mechanism allows only one stream per minor device. The driver open routine is called whenever a STREAMS device is opened. open matches the correct private data structure with the stream using ddi_get_soft_state(9F). The driver open, lpopen in Example 9–4, has the same interface as the module open.
The stream flag, sflag, must have the value 0, indicating a normal driver open. devp pointers to the major/minor device number for the port. After checking sflag, lpopen uses devp to find the correct soft-state structure.
The next check, if (q->q_ptr)..., determines if the printer is already open. q_ptr is a driver or module private data pointer. It can be used by the driver for any purpose and is initialized to zero by STREAMS before the first open. In this example, the driver sets the value of q_ptr, in both the read and write queue structures, to point to the device's per-instance data structure. If the pointer is non-NULL, it means the printer is already open, so lpopen returns EBUSY to avoid merging printouts from multiple users.
The driver close routine is called by the stream head. Any messages left in the queue are automatically removed by STREAMS. The stream is dismantled and data structures are released.
/*ARGSUSED*/ static int lpopen( queue_t *q, /* read queue */ dev_t *devp, int flag, int sflag, cred_t *credp) { struct lp *lp; if (sflag) /* driver refuses to do module or clone open */ return (ENXIO); if ((lp = ddi_get_soft_state(lp_state, getminor(*devp))) == NULL) return (ENXIO); /* Check if open already. Can't have multiple opens */ if (q->q_ptr) { return (EBUSY); } lp->qptr = WR(q); q->q_ptr = (char *) lp; WR(q)->q_ptr = (char *) lp; qprocson(q); return (0); } /*ARGSUSED*/ static int lpclose( queue_t *q, /* read queue */ int flag, cred_t *credp) { struct lp *lp; qprocsoff(q); lp = (struct lp *) q->q_ptr; /* * Free message, queue is automatically * flushed by STREAMS */ mutex_enter(&lp->lp_lock); if (lp->msg) { freemsg(lp->msg); lp->msg = NULL; } lp->flags = 0; mutex_exit(&lp->lp_lock); return (0); }
There are no physical pointers between the read and write queue of a pair. WR(9F) is a queue pointer function. WR(9F) generates the write pointer from the read pointer. RD(9F) and otherq(9F) are additional queue pointer functions. RD(9F) generates the read pointer from the write pointer, and otherq(9F) generates the mate pointer from either.
The write put procedure in Example 9–5, lpwput, illustrates driver M_FLUSH handling. Note that all drivers are expected to incorporate flush handling.
If FLUSHW is set, the write message queue is flushed, and (in this example) the leading message (lp->msg) is also flushed. lp_lock protects the driver's per-instance data structure.
In most drivers, if FLUSHR is set, the read queue is flushed. However, in this example, no messages are ever placed on the read queue, so flushing it is not necessary. The FLUSHW bit is cleared and the message is sent upstream using qreply(9F). If FLUSHR is not set, the message is discarded.
The stream head always performs the following actions on flush requests received on the read side from downstream. If FLUSHR is set, messages waiting to be sent to user space are flushed. If FLUSHW is set, the stream head clears the FLUSHR bit and sends the M_FLUSH message downstream. In this manner, a single M_FLUSH message sent from the driver can reach all queues in a stream. A module must send two M_FLUSH messages to have the same effect.
lpwput queues M_DATA and M_IOCTL messages and if the device is not busy, starts output by calling lpout. Message types that are not recognized are discarded (in the default case of the switch).
static int lpwput( queue_t *q, /* write queue */ mblk_t *mp) /* message pointer */ { struct lp *lp; lp = (struct lp *)q->q_ptr; switch (mp->b_datap->db_type) { default: freemsg(mp); break; case M_FLUSH: /* Canonical flush handling */ if (*mp->b_rptr & FLUSHW) { flushq(q, FLUSHDATA); mutex_enter(&lp->lp_lock); /* lock any access to lp */ if (lp->msg) { freemsg(lp->msg); lp->msg = NULL; } mutex_exit(&lp->lp_lock); } if (*mp->b_rptr & FLUSHR) { *mp->b_rptr &= ~FLUSHW; qreply(q, mp); } else freemsg(mp); break; case M_IOCTL: case M_DATA: (void) putq(q, mp); mutex_enter(&lp->lp_lock); if (!(lp->flags & BUSY)) lpout(lp); mutex_exit(&lp->lp_lock); } return (0); }
Example 9–6 shows the interrupt handling for the printer driver.
lpintr is the driver-interrupt handler registered by the attach routine.
lpout takes a single character from the queue and sends it to the printer. For convenience, the message currently being output is stored in lp->msg in the per-instance structure. This assumes that the message is called with the mutex held.
lpoutchar sends a single character to the printer (in this case the system console using cmn_err(9F)) and interrupts when complete. Of course, hardware would generate a hard interrupt, so the call to ddi_trigger_softintr(9F) would be unnecessary.
/* Device interrupt routine */static uint lpintr(caddr_t lp) /* minor device number of lp */ { struct lp *lpp = (struct lp *)lp; mutex_enter(&lpp->lp_lock); if (!(lpp->flags & BUSY)) { mutex_exit(&lpp->lp_lock); return (DDI_INTR_UNCLAIMED); } lpp->flags &= ~BUSY; lpout(lpp); mutex_exit(&lpp->lp_lock); return (DDI_INTR_CLAIMED); } /* Start output to device - used by put procedure and driver */ static void lpout( struct lp *lp) { mblk_t *bp; queue_t *q; q = lp->qptr; loop: if ((bp = lp->msg) == NULL) { /*no current message*/ if ((bp = getq(q)) == NULL) { lp->flags &= ~BUSY; return; } if (bp->b_datap->db_type == M_IOCTL) { /* lpdoioctl(lp, bp); */ goto loop; } lp->msg = bp; /* new message */ } if (bp->b_rptr >= bp->b_wptr) { /* validate message */ bp = lp->msg->b_cont; lp->msg->b_cont = NULL; freeb(lp->msg); lp->msg = bp; goto loop; } lpoutchar(lp, *bp->b_rptr++); /*output one character*/ lp->flags |= BUSY; } static void lpoutchar( struct lp *lp, char c) { cmn_err(CE_CONT, "%c", c); ddi_trigger_softintr(lp->siid); }
The same utilities (described in Chapter 10, STREAMS Modules) and mechanisms used for module flow control are used by drivers.
When the message is queued, putq(9F) increments the value of q_count by the size of the message and compares the result to the driver's write high-watermark (q_hiwat) value. If the count reaches q_hiwat, putq(9F) sets the internal FULL indicator for the driver write queue. This causes messages from upstream to be halted (canputnext(9F) returns FALSE) until the write queue count drops below q_lowat. The driver messages waiting to be output through lpout are dequeued by the driver output interrupt routine with getq(9F), which decrements the count. If the resulting count is below q_lowat, getq(9F) back-enables any upstream queue that had been blocked.
For priority band data, qb_count, qb_hiwat, and qb_lowat are used.
STREAMS with flow control can be used on the driver read side to handle temporary upstream blocks.
To some extent, a driver or a module can control when its upstream transmission becomes blocked. Control is available through the M_SETOPTS message (see Appendix A, Message Types) to modify the stream head read-side flow control limits.
To eliminate polling, STREAMS drivers can be made clonable. If a STREAMS driver is implemented as a clonable device, a single node in the file system can be opened to access any unused device that the driver controls. This special node guarantees that each user is allocated a separate stream to the driver for each open call. Each stream is associated with an unused minor device, so the total number of streams that may be connected to a particular clonable driver is limited only by the number of minor devices configured for that driver.
In previous examples, each user process connected a stream to a driver by explicitly opening a particular minor device of the driver. Each minor device had its own node in the device tree file system. Often, there is a need for a user process to connect a new stream to a driver regardless of which minor device is used to access the driver. In the past, this forced the user process to poll the various minor device nodes of the driver for an available minor device.
The clone model is useful, for example, in a networking environment where a protocol pseudo-device driver requires each user to open a separate stream over which it establishes communication. (The decision to implement a STREAMS driver as a clonable device is made by the designers of the device driver. Knowledge of the clone driver implementation is not required to use it.)
There are two ways to open as a clone device. The first is to use the STREAMS framework-provided clone device, which arranges to open the device with the CLONEOPEN flag passed in. This method is demonstrated in Example 9–7, which shows the attach and open routines for the pseudo-terminal master ptm(7D) driver. The second way is to have the driver open itself as a clone device, without intervention from the system clone device. This method is demonstrated in the attach and open routines for the log(7D) device in Example 9–8.
The ptm(7D) device, which uses the system-provided clone device, sets up two nodes in the device file system. One has a major number of 23 (ptm's assigned major number) and a minor number of 0. The other node has a major number of 11 (the clone device's assigned major number) and a minor number of 23 (ptm's assigned major number). The driver's attach routine (see Example 9–7) calls to ddi_create_minor_node(9F) twice. First, to set up the "normal" node (major number 23); second, to specify CLONE_DEV as the last parameter, making the system create the node with major 11.
crw-rw-rw- 1 sys 11, 23 Mar 6 02:05 clone:ptmx crw------- 1 sys 23, 0 Mar 6 02:05 ptm:ptmajor
When the special file /devices/pseudo/clone@0:ptmx is opened, the clone driver code in the kernel (accessed by major 11) passes the CLONEOPEN flag in the sflag parameter to the ptm(7D) open routine. ptm's open routine checks sflag to make sure it is being called by the clone driver. The open routine next attempts to find an unused minor device for the open by searching its table of minor devices. (PT_ENTER_WRITE and PT_EXIT_WRITE are driver-defined macros for entering and exiting the driver's mutex.) If it succeeds (and following other open processing), the open routine constructs a new dev_t with the new minor number, which it passes back to its caller in the devp parameter. (The new minor number is available to the user program that opened the clonable device through an fstat(2) call.)
static int ptm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { if (cmd != DDI_ATTACH) return (DDI_FAILURE); if (ddi_create_minor_node(devi, "ptmajor", S_IFCHR, 0, NULL, 0) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } if (ddi_create_minor_node(devi, "ptmx", S_IFCHR, 0, NULL, CLONE_DEV) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } ptm_dip = devi; return (DDI_SUCCESS); } static int ptmopen( queue_t *rqp, /* pointer to the read side queue */ dev_t *devp, /* pointer to stream tail's dev */ int oflag, /* the user open(2) supplied flags */ int sflag, /* open state flag */ cred_t *credp) /* credentials */ { struct pt_ttys *ptmp; mblk_t *mop; /* ptr to a setopts message block */ minor_t dev; if (sflag != CLONEOPEN) { return (EINVAL); } for (dev = 0; dev < pt_cnt; dev++) { ptmp = &ptms_tty[dev]; PT_ENTER_WRITE(ptmp); if (ptmp->pt_state & (PTMOPEN | PTSOPEN | PTLOCK)) { PT_EXIT_WRITE(ptmp); } else break; } if (dev >= pt_cnt) { return (ENODEV); } ... <other open processing> ... /* * The input, devp, is a major device number, the output is put into * into the same parm as a major,minor pair. */ *devp = makedevice(getmajor(*devp), dev); return (0); }
The log(7D) driver uses the second method; it clones itself without intervention from the system clone device. The log(7D) driver's attach routine (in Example 9–8) is similar to the one in ptm(7D). It creates two nodes using ddi_create_minor_node(9F), but neither specifies CLONE_DEV as the last parameter. Instead, one of the devices has minor 0, the other minor CLONEMIN. These two devices provide log(7D) two interfaces: the first write-only, the second read-write (see the man page log(7D) for more information). Users open one node or the other. If they open the CONSWMIN (clonable, read-write) node, the open routine checks its table of minor devices for an unused device. If it is successful, it (like the ptm(7D) open routine) returns the new dev_t to its caller in devp.
static int log_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { if (ddi_create_minor_node(devi, "conslog", S_IFCHR, 0, NULL, NULL) == DDI_FAILURE || ddi_create_minor_node(devi, "log", S_IFCHR, 5, NULL, NULL) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); return (-1); } log_dip = devi; return (DDI_SUCCESS); } static int logopen( queue_t *q, dev_t *devp, int flag, int sflag, cred_t *cr) { int i; struct log *lp; /* * A MODOPEN is invalid and so is a CLONEOPEN. * This is because a clone open comes in as a CLONEMIN device open!! */ if (sflag) return (ENXIO); mutex_enter(&log_lock); switch (getminor(*devp)) { case CONSWMIN: if (flag & FREAD) { /* you can only write to this minor */ mutex_exit(&log_lock); return (EINVAL); } if (q->q_ptr) { /* already open */ mutex_exit(&log_lock); return (0); } lp = &log_log[CONSWMIN]; break; case CLONEMIN: /* * Find an unused minor > CLONEMIN. */ i = CLONEMIN + 1; for (lp = &log_log[i]; i < log_cnt; i++, lp++) { if (!(lp->log_state & LOGOPEN)) break; } if (i >= log_cnt) { mutex_exit(&log_lock); return (ENXIO); } *devp = makedevice(getmajor(*devp), i); /* clone it */ break; default: mutex_exit(&log_lock); return (ENXIO); } /* * Finish device initialization. */ lp->log_state = LOGOPEN; lp->log_rdq = q; q->q_ptr = (void *)lp; WR(q)->q_ptr = (void *)lp; mutex_exit(&log_lock); qprocson(q); return (0); }
The loop-around driver is a pseudo-driver that loops data from one open stream to another open stream. The associated files are almost like a full-duplex pipe to user processes. The streams are not physically linked. The driver is a simple multiplexer that passes messages from one stream's write queue to the other stream's read queue.
To create a connection, a process opens two streams, obtains the minor device number associated with one of the returned file descriptors, and sends the device number in an ioctl(2) to the other stream. For each open, the driver open places the passed queue pointer in a driver interconnection table, indexed by the device number. When the driver later receives an M_IOCTL message, it uses the device number to locate the other stream's interconnection table entry, and stores the appropriate queue pointers in both of the streams' interconnection table entries.
Subsequently, when messages other than M_IOCTL or M_FLUSH are received by the driver on either stream's write side, the messages are switched to the read queue following the driver on the other stream's read side. The resultant logical connection is shown in Figure 9–2. Flow control between the two streams must be handled explicitly, since STREAMS do not automatically propagate flow control information between two streams that are not physically connected.
Example 9–9 shows the loop-around driver code. The loop structure contains the interconnection information for a pair of streams. loop_loop is indexed by the minor device number. When a stream is opened to the driver, the driver places the address of the corresponding loop_loop element in the q_ptr (private data structure pointer) of the read-side and write-side queues. Since STREAMS clears q_ptr when the queue is allocated, a NULL value of q_ptr indicates an initial open. loop_loop verifies that this stream is connected to another open stream.
The code presented here for the loop-around driver represents a single-threaded, uniprocessor implementation. Chapter 12, Multithreaded STREAMS presents multiprocessor and multithreading issues such as locking to prevent race conditions and data corruption.
Example 9–9 contains the declarations for the driver.
/* Loop-around driver */ #include <sys/types.h> #include <sys/param.h> #include <sys/stream.h> #include <sys/stropts.h> #include <sys/signal.h> #include <sys/errno.h> #include <sys/cred.h> #include <sys/stat.h> #include <sys/modctl.h> #include <sys/conf.h> #include <sys/ddi.h> #include <sys/sunddi.h> static int loop_identify(dev_info_t *); static int loop_attach(dev_info_t *, ddi_attach_cmd_t); static int loop_detach(dev_info_t *, ddi_detach_cmd_t); static int loop_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); static int loopopen (queue_t*, dev_t*, int, int, cred_t*); static int loopclose (queue_t*, int, cred_t*); static int loopwput (queue_t*, mblk_t*); static int loopwsrv (queue_t*); static int looprsrv (queue_t*); static dev_info_t *loop_dip; /* private devinfo pointer */ static struct module_info minfo = { 0xee12, "loop", 0, INFPSZ, 512, 128 }; static struct qinit rinit = { (int (*)()) NULL, looprsrv, loopopen, loopclose, (int (*)()) NULL, &minfo, NULL }; static struct qinit winit = { loopwput, loopwsrv, (int (*)()) NULL, (int (*)()) NULL, (int (*)()) NULL, &minfo, NULL }; static struct streamtab loopinfo= { &rinit, &winit, NULL, NULL }; struct loop { queue_t *qptr; /* back pointer to write queue */ queue_t *oqptr; /* pointer to connected read queue */ }; #define LOOP_CONF_FLAG (D_NEW | D_MP) static struct cb_ops cb_loop_ops = { nulldev, /* cb_open */ nulldev, /* cb_close */ nodev, /* cb_strategy */ nodev, /* cb_print */ nodev, /* cb_dump */ nodev, /* cb_read */ nodev, /* cb_write */ nodev, /* cb_ioctl */ nodev, /* cb_devmap */ nodev, /* cb_mmap */ nodev, /* cb_segmap */ nochpoll, /* cb_chpoll */ ddi_prop_op, /* cb_prop_op */ ( &loopinfo), /* cb_stream */ (int)(LOOP_CONF_FLAG) /* cb_flag */ }; static struct dev_ops loop_ops = { DEVO_REV, /* devo_rev */ 0, /* devo_refcnt */ (loop_devinfo), /* devo_getinfo */ (loop_identify), /* devo_identify */ (nulldev), /* devo_probe */ (loop_attach), /* devo_attach */ (loop_detach), /* devo_detach */ (nodev), /* devo_reset */ &(cb_loop_ops), /* devo_cb_ops */ (struct bus_ops *)NULL, /* devo_bus_ops */ (int (*)()) NULL /* devo_power */ }; #define LOOP_SET ((`l'<<8)|1) /* in a .h file */ #define NLOOP 64 static struct loop loop_loop[NLOOP]; static int loop_cnt = NLOOP; /* * Module linkage information for the kernel. */ extern struct mod_ops mod_strmodops; static struct modldrv modldrv = { &mod_driverops, "STREAMS loop driver", &loop_ops }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; _init() { return (mod_install(&modlinkage)); } _info(modinfop) struct modinfo *modinfop; { return (mod_info(&modlinkage, modinfop)); } _fini(void) { return (mod_remove(&modlinkage)); }
Example 9–10 contains the initialization routines.
static int loop_identify(dev_info_t *devi) { if (strcmp(ddi_get_name(devi), "loop") == 0) return (DDI_IDENTIFIED); else return (DDI_NOT_IDENTIFIED); } static int loop_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { if (cmd != DDI_ATTACH) return (DDI_FAILURE); if (ddi_create_minor_node(devi, "loopmajor", S_IFCHR, 0, NULL, 0) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } if (ddi_create_minor_node(devi, "loopx", S_IFCHR, 0, NULL, CLONE_DEV) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); return (DDI_FAILURE); } loop_dip = devi; return (DDI_SUCCESS); } static int loop_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) { if (cmd != DDI_DETACH) return (DDI_FAILURE); ddi_remove_minor_node(devi, NULL); return (DDI_SUCCESS); } /*ARGSUSED*/ static int loop_devinfo( dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int error; switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if (loop_dip == NULL) { error = DDI_FAILURE; } else { *result = (void *) loop_dip; error = DDI_SUCCESS; } break; case DDI_INFO_DEVT2INSTANCE: *result = (void *)0; error = DDI_SUCCESS; break; default: error = DDI_FAILURE; } return (error); }
The open procedure (in Example 9–11) includes canonical clone processing that enables a single file system node to yield a new minor device/vnode each time the driver is opened. In loopopen, sflag can be CLONEOPEN, indicating that the driver picks an unused minor device. In this case, the driver scans its private loop_loop data structure to find an unused minor device number. If sflag is not set to CLONEOPEN, the passed-in minor device specified by getminor(*devp) is used.
/*ARGSUSED*/ static int loopopen( queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) { struct loop *loop; minor_t newminor; if (q->q_ptr) /* already open */ return(0); /* * If CLONEOPEN, pick a minor device number to use. * Otherwise, check the minor device range. */ if (sflag == CLONEOPEN) { for (newminor = 0; newminor < loop_cnt; newminor++ ) { if (loop_loop[newminor].qptr == NULL) break; } } else newminor = getminor(*devp); if (newminor >= loop_cnt) return(ENXIO); /* * construct new device number and reset devp * getmajor gets the major number */ *devp = makedevice(getmajor(*devp), newminor); loop = &loop_loop[newminor]; WR(q)->q_ptr = (char *) loop; q->q_ptr = (char *) loop; loop->qptr = WR(q); loop->oqptr = NULL; qprocson(q); return(0); }
Because the messages are switched to the read queue following the other stream's read side, the driver needs a put procedure only on its write side. loopwput (in Example 9–12) shows another use of an ioctl(2). The driver supports the ioc_cmd value LOOP_SET in the iocblk(9S) of the M_IOCTL message. LOOP_SET makes the driver connect the current open stream to the stream indicated in the message. The second block of the M_IOCTL message holds an integer that specifies the minor device number of the stream to which to connect.
The LOOP_SET ioctl(2) processing involves several checks:
Does the second block have the proper amount of data?
Is the "to" device in range?
Is the "to" device open?
Is the current stream disconnected?
Is the "to" stream disconnected?
If these checks pass, the read queue pointers for the two streams are stored in the respective oqptr fields. This cross-connects the two streams indirectly, through loop_loop.
The put procedure incorporates canonical flush handling.
loopwput queues all other messages (for example, M_DATA or M_PROTO) for processing by its service procedure. A check is made that the stream is connected. If not, M_ERROR is sent to the stream head. Certain message types can be sent upstream by drivers and modules to the stream head where they are translated into actions detectable by user processes. These messages may also modify the state of the stream head:
Causes the stream head to lock up. Message transmission between stream and user processes is terminated. All subsequent system calls except close(2) and poll(2) fail. Also causes M_FLUSH, clearing all message queues, to be sent downstream by the stream head.
Terminates input from a user process to the stream. All subsequent system calls that would send messages downstream fail. Once the stream head read message queue is empty, EOF is returned on reads. This can also result in SIGHUP being sent to the process group's session leader.
Causes a specified signal to be sent to the process group associated with the stream.
putnextctl(9F) and putnextctl1(9F) allocate a nondata (that is, not M_DATA, M_DELAY, M_PROTO, or M_PCPROTO) type message, place one byte in the message (for putnextctl1(9F)), and call the put(9E) procedure of the specified queue.
static int loopwput(queue_t *q, mblk_t *mp) { struct loop *loop; int to; loop = (struct loop *)q->q_ptr; switch (mp->b_datap->db_type) { case M_IOCTL: { struct iocblk *iocp; int error=0; iocp = (struct iocblk *)mp->b_rptr; switch (iocp->ioc_cmd) { case LOOP_SET: { /* * if this is a transparent ioctl return an error; * the complete solution is to convert the message * into an M_COPYIN message so that the data is * ultimately copied from user space * to kernel space. */ if (iocp->ioc_count == TRANSPARENT) { error = EINVAL; goto iocnak; } /* fetch other minor device number */ to = *(int *)mp->b_cont->b_rptr; /* * Sanity check. ioc_count contains the amount * of user supplied data which must equal the * size of an int. */ if (iocp->ioc_count != sizeof(int)) { error = EINVAL; goto iocnak; } /* Is the minor device number in range? */ if (to >= loop_cnt || to < 0) { error = ENXIO; goto iocnak; } /* Is the other device open? */ if (!loop_loop[to].qptr) { error = ENXIO; goto iocnak; } /* Check if either dev is currently connected */ if (loop->oqptr || loop_loop[to].oqptr) { error = EBUSY; goto iocnak; } /* Cross connect the streams through * the loopstruct */ loop->oqptr = RD(loop_loop[to].qptr); loop_loop[to].oqptr = RD(q); /* * Return successful ioctl. Set ioc_count * to zero, since no data is returned. */ mp->b_datap->db_type = M_IOCACK; iocp->ioc_count = 0; qreply(q, mp); break; } default: error = EINVAL; iocnak: /* * Bad ioctl. Setting ioc_error causes * the ioctl call to return that particular errno. * By default, ioctl returns EINVAL on failure. */ mp->b_datap->db_type = M_IOCNAK; iocp->ioc_error = error; qreply(q, mp); break; } break; } case M_FLUSH: { if (*mp->b_rptr & FLUSHW) { flushq(q, FLUSHALL); /* write */ if (loop->oqptr) flushq(loop->oqptr, FLUSHALL); /* read on other side equals write on this side */ } if (*mp->b_rptr & FLUSHR) { flushq(RD(q), FLUSHALL); if (loop->oqptr != NULL) flushq(WR(loop->oqptr), FLUSHALL); } switch(*mp->b_rptr) { case FLUSHW: *mp->b_rptr = FLUSHR; break; case FLUSHR: *mp->b_rptr = FLUSHW; break; } if (loop->oqptr != NULL) (void) putnext(loop->oqptr, mp); break; } default: /* If this Stream isn't connected, send * M_ERROR upstream. */ if (loop->oqptr == NULL) { freemsg(mp); (void) putnextctl1(RD(q), M_ERROR, ENXIO); break; } (void) putq(q, mp); } return (0); }
Service procedures are required in this example on both the write side and read side for flow control (see Example 9–13). The write service procedure, loopwsrv, takes on the canonical form. The queue being written to is not downstream, but upstream (found through oqptr) on the other stream.
In this case, there is no read-side put procedure so the read service procedure, looprsrv, is not scheduled by an associated put procedure, as has been done previously. looprsrv is scheduled only by being back-enabled when its upstream flow control blockage is released. The purpose of the procedure is to re-enable the writer (loopwsrv) by using oqptr to find the related queue. loopwsrv cannot be directly back-enabled by STREAMS because there is no direct queue linkage between the two streams. Note that no message is queued to the read service procedure. Messages are kept on the write side so that flow control can propagate up to the stream head. qenable(9F) schedules the write-side service procedure of the other stream.
staticintloopwsrv (queue_t*q) { mblk_t *mp; structloop *loop; loop = (structloop*) q->q_ptr; while ((mp = getq (q)) != NULL){ /* Check if we can put the message up * the other Stream read queue */ if (mp->b_datap->db_type <= QPCTL && !canputnext (loop->oqptr)) { (void) putbq (q,mp); /*read-side is blocked*/ break; } /* send message to queue following other Stream read queue */ (void) putnext (loop->oqptr, mp); } return (0); } staticintlooprsrv (queue_t*q) { /* Enter only when "backenabled" by flow control */ structloop *loop; loop = (structloop*) q->q_ptr; if (loop->oqptr == NULL) return (0); /*manually enable write service procedure*/ qenable (WR (loop->oqptr)); return (0); }
loopclose breaks the connection between the streams, as shown in Example 9–14. loopclose sends an M_HANGUP message up the connected stream to the stream head.
/*ARGSUSED*/ static int loopclose ( queue_t *q, int flag, cred_t *credp) { struct loop *loop; loop = (struct loop *)q->q_ptr; loop->qptr = NULL; /* * If we are connected to another stream, break the linkage, and * send a hangup message. The hangup message causes the stream head * to reject writes, allow the queued data to be read completely, * and then return EOF on subsequent reads. */ if (loop->oqptr) { (void) putnextctl(loop->oqptr, M_HANGUP); ((struct loop *)loop->oqptr->q_ptr)->oqptr = NULL; loop->oqptr = NULL; } qprocsoff(q); return (0); }
An application using this driver would first open the clone device node created in the attach routine (/devices/pseudo/clone@0:loopx) two times to obtain two streams. The application can determine the minor numbers of the devices by using fstat(2). Next, it joins the two streams by using the STREAMS I_STR ioctl(2) (see streamio(7I)) to pass the LOOP_SET ioctl(2) with one of the stream's minor numbers as an argument to the other stream. Once this is completed, the data sent to one stream using write(2) or putmsg(2) can be retrieved from the other stream with read(2) or getmsg(2). The application also can interpose STREAMS modules between the stream heads and the driver using the I_PUSH ioctl(2).
STREAMS device drivers are in many ways similar to non-STREAMS device drivers. The following points summarize the differences between STREAMS drivers and other drivers:
Drivers must have attach(9E) and probe(9E) entry points to initialize the driver. The attach routine initializes the driver. Software drivers usually have little to initialize, because there is no hardware involved.
Most drivers have an interrupt handler routine. The driver developer is responsible for supplying an interrupt routine for the device's driver. In addition to hardware interrupts, the system also supports software interrupts. A software interrupt is generated by calling ddi_trigger_softintr(9F).
All minor nodes are generated by ddi_create_minor_node(9F).
STREAMS device drivers also are similar to STREAMS modules. The following points summarize some of the differences between STREAMS modules and drivers.
Messages that are not understood by the drivers should be freed.
A driver must process all M_IOCTL messages. Otherwise, the stream head blocks for an M_IOCNAK, M_IOCACK, or until the timeout (potentially infinite) expires.
If a driver does not understand an ioctl(2), an M_IOCNAK message must be sent upstream.
The stream head locks up the stream when it receives an M_ERROR message, so driver developers should be careful when using the M_ERROR message.
A hardware driver must provide an interrupt routine.
Multithreaded drivers must protect their own data structures.
For more information on global driver issues and non-STREAMS drivers, see Writing Device Drivers.


text:
Whether you've played music before or not, learn how to start a song in GarageBand using Live Loops in this 30-minute session. You'll get hands-on with prerecorded music patterns on iPad to find your groove. Bring your iPad or we'll provide one. Recommended for beginners.
12531 Wayzata Boulevard
Minnetonka, MN 55305

subset: commoncrawl


text:
100 Qualifying slots plus 25 Women For Tri SlotsRegister ▻
Accommodation in and around Taupo does fill up over race weekend so please book and confirm your accommodation prior to your arrival in Taupo.
Some of our preferred accommodation providers are:
Suncourt Hotel
14 Northcroft St, Taupo
Website: www.suncourt.nz
Booking enquiries: [email protected]
|Lake Taupo Hilton
100/80 Napier-Taupo Rd, Hilltop, Taupo
Website: www.hilton.com/Taupo
Booking enquiries: [email protected]
|Wairakei Resort
640 Wairakei Road, Taupo
Website: https://www.wairakei.co.nz
Booking enquiries: [email protected]
|Prince Motor Lodge
100 Roberts St, Taupo
Website: www.princemotorlodge.co.nz
Booking enquiries: [email protected]
|Centennial House
432 Centennial Drive, Taupo
Website: www.centennialhousetaupo.co.nz
Booking enquiries: +6421912743
The Taupo Visitors Centre can also help you with any independent bookings. You can contact them directly if you have any questions on Email: [email protected] or see the Lake Taupo website.
Say 'Yeah!' with our Accommodation Partner Booking.com. Best Value. Best Range. Best Rewards.


text:
- Features
- News
- Arts
- Life
- Community
- Nightlife
- Scene
- Social
The Washington woman accused of shooting a gay man at an IHOP restaurant in Columbia Heights last year, during an altercation where she and two friends allegedly made derogatory comments toward the man and his companions, was arraigned on seven charges Wednesday in D.C. Superior Court.
LaShawn Carson, who was indicted on seven different charges Feb. 6, was arraigned on a count of aggravated assault while knowingly armed; one count of assault with a dangerous weapon; two counts of possession of a firearm during a crime of violence; one count of unlawful possession of a firearm; one count of unlawful possession of ammunition; and one count of carrying a pistol without a license.
D.C. Superior Court Judge Michael Ryan scheduled Carson to go to trial June 6, more than 14 months after she was first arrested for her alleged role in the IHOP shooting.
Carson's lawyer, Patrick J. Christmas II, asked that the terms of Carson's release be reduced in severity due to good behavior. Ryan granted the request by changing the terms of Carson's release from home confinement to high-intensity supervision, noting that Carson had been in "perfect compliance," with no drug-testing violations, curfew violations or failures to report to authorities, according to a Feb. 13 report from the court's Pretrial Services Agency (PSA), which monitors and tracks defendants awaiting trial.
Under the previous release conditions, Carson was allowed out of her home only for work, otherwise confined to her Columbia Heights apartment. The new conditions do not subject her to home confinement, though Carson will still have a court-imposed curfew of 10 p.m. to 6 a.m. when she cannot leave her home. A court order requiring her to stay away from the victim remains in effect.
According to charging documents, Carson and her two companions were in the IHOP during the early morning hours of March 11, 2012, when they allegedly began taunting the gay victim and his companions. A physical confrontation ensued between the victim and one of Carson's acquaintances. The two men were being separated by an off-duty police officer when a gun was fired, the victim was shot and Carson and her two companions fled the scene.
Carson was arrested March 26, 2012, following a two-week investigation by the Metropolitan Police Department. The victim, who sustained injuries to his abdomen, was transported to Washington MedStar Hospital, treated and released.


text:
Joe Hill, the best-selling author of Horns, makes his debut in Canada next Monday, March 22 at the Toronto Public Library – Lillian H. Smith Library, The Merril Collection (239 College St.). For more information, call 416-963-9993. He will be signing copies of his book.
Look for an interview with Hill in the day following this event.


text:
Saturday, 14 May 2011
Copenhagen Marathon
I am, however, breaking one of my cardinal rules of running in prenancy. Yes, whether or not you want to know, I am going to tell you what those rules are:
1. Cross train
2. Do intense core exercises 2-3 times a week, focussing on back and thighs
3. Run almost exclusively on trails (roads serve only as access to trails)
4. Run in minimalist trail shoes
So yeah, running a marathon on exclusively hard surfaces is not the brightest idea if I want to make it through this pregnancy uninjured. BUT:
1. The excitement of a big city marathon is hard to resist (plus it is kind of my city)
2. It is a flat, easy route
3. I can sign up for half price because I am a student at the University of Copenhagen
4. SR's parents offered to watch the kids
5. I will be in Copenhagen anyway the night before with SR, who is on call over the weekend
6. I went on a pain-free, wonderful 3 hour run today, making me think the marathon distance is still manageable at this point in pregnancy
7. I will be at a sports medicine course all next week so (ironically enough) I'll barely have time to exercise
8. It's a chance to see friends
9. I love the idea of people smiling at the big belly (yes, I admit it)
All of these stars aligning made me realize I couldn't NOT run it. Plus, if anything hurts, I'll just stop (right?).
And most importantly, I have to remind myself over and over: One should never run to prove anything. Well, other people can; sure, that's up to them. But I think the only healthy way of looking at it is - One should run because one loves to run. And if I look at it that way, I am simply excited for next Sunday.
4 comments:
- cherelli said...
Sweet! If you think you can do it and want to start, why not? You can go out there and enjoy soaking up the atmosphere at a comfortable relaxed pace...and just see how it goes...no one says you have to finish, just enjoy the day for what it is - a nice run among thousands of friends! I hope it's enjoyable for you - and I hope that if anything doesn't feel "quite right" you will know to simply walk away :) Have fun!
- 14 May 2011 at 18:29
- Kirsten said...
I'm sorry, but I have this thing about running to prove something. To prove that I can. Overcome my fear of the marathon distance. That I can do something that nobody ever thought that I can do. And you go running girl, this marathon and prove that you can. Even if you are quite pregnant by this stage. You shall and will glow with happiness when crossing the finish line, tummy first! You have run so many races, take this one to enjoy the ride. Never mind the time - you have enough achievements. Wish I could run with you and enjoy seeing you shine!
- 14 May 2011 at 20:51
- cherelli said...
Hey SLG, Someone posted this on the RunnersWorld forum...I think you may have commented on it last year - or maybe it was a different study? Anyway, I'm interested in your take on the data when you get a chance....esp as it relates to how much time is spent exericing, though it doesn't mention intensity or how much people exercised going into pregnancy...http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2366024/?tool=pmcentrez
- 18 May 2011 at 16:53
- sea legs girl said...
Hi Cherelli!
Actually, I hadn't read that study. Made me think a bit, but mostly made me wonder what affects what ---
Do nausea & fatigue (other signs of a healthy pregnancy) lead to less exercise or does exercise increase the risk of miscarriage? It's a tough question to answer and in my mind makes the study really hard to interpret.
In my experience, I exercised quite a bit more the time I miscarried than this time at the beginning of pregnancy. If you ask me, it's because I felt totally unaffected by the pregnancy the miscarriage time.
- 18 May 2011 at 17:14


text:
Multi-Purpose OnePage Parallax HTML5 theme!
Rise is a fully responsive, single page HTML5 & CSS3 Template that offers stunning design, really great compatibility for agency, personal portfolio, corporate, freelancer, general business and app landing page etc;
With tons of options and elite support create a unique and perfect website for any kind of project.
- Unlimited layout Possibilities
- Animated Sticky navigation
- Gorgeous design
- 4 color skins
- Awesome & useful parallax sections
- Semantic and valid code
- isotope filter for portfolio
- Ajax Portfolio Detail
- Pricing table and Services design
- Cool & smooth CSS3 animations
- Premium Revolution Slider
- Blog page
- Fully responsive
- Smooth scrolling
- Functional contact form
- Testimonials Scroll
- Font Awesome icons support
- Unique team section design
- Social icons with tool tip
- Parallax effects
- Accordion and Tabs
- Extremely easy to setup
- Compatible with all major browsers
NOTE: Images used in the demo are not included for download, these images are copyrighted, if you are planning to use the photos we can provide the links to buy license.


text: According to the manufacturer, the Xdream is the "first indoor fitness product that accurately simulates the experience of riding outdoors." Basically, the bike thrusts you into a virtual world via the attached computer screen. As you ride, the full experience on the screen is translated to the equipment—right down to the track surfaces and conditions. Plus, it utilizes a full range of muscles in the upper body and lower body, which makes for a more complete workout. Pricing isn't advertised, but expect it to set you back many thousands of dollars. Alternatively, you could grab a bike and actually go outside. [Xdream]


text:
"Revolution" fans finally saw what's behind that scary red door at the end of Wednesday's episode when Titus Andover's thugs dragged Miles through it. (SPOILERS if you're not caught up!)
"Yeah, that's really bad news for Miles," Deborah Puette teases. "It's very bad news."
Puette, a former Chicago theater actress and go-to gal for playing "dying woman in bed," stars as Jessica Andover—that woman dying in bed at the end of "There Will Be Blood."
Puette is sworn to secrecy about the bizarre ritual that's obviously taking place, but viewers just need to add up all the clues from the episode—Jessica's bloodied arm, the dead man on the gurney and the "doctor" standing over him cleaning a needle.
There will be blood transfusions, right?
"Her husband has contrived a really twisted way to keep his beloved wife alive," Puette said. "Jessica becomes key to the survival of the entire town of Willoughby, and of Rachel (Elizabeth Mitchell) and Miles (Billy Burke) and the rest of them."
They're all fighting for their survival in next week's episode, "Love Story." In the preview above, creepy Titus (Matt Ross) and the doctor strap Miles onto that gurney, and Titus' forces attack Willoughby, shooting Rachel with an arrow.
The episode just might get its title from the story of Titus and Jessica.
"He really does love her a great deal. It's just the way he loves her is really twisted," Puette said of the David Rambo written episode. "It's a love story, like in the worst sense of the term. It's really dark."
If you're a fan of "True Blood," Puette's bedridden scene Wednesday might have reminded you of her scenes in the Season 5 "True Blood" episode, "Somebody That I Used to Know." Puette played Sarah Compton-Harris, the dying daughter of vampire Bill (Stephen Moyer) who begged him to turn her.
"It was so, so freaky because … my costume is a very similar color, I'm clearly sick in bed, and it was an old-fashioned kind of setting--something that looks like a period piece. It did seem very similar," she said. "Maybe I'm going to just corner the market on playing dying ladies."
Does her character die this time? Keep watching "Revolution" at 7 p.m. CT Wednesdays on NBC. Puette will answer fan questions during the West Coast airing of the Oct. 9 episode. Tweet her @deborahpuette.
Want more? Discuss this article and others on Show Patrol's Facebook page.
"Revolution" fans finally saw what's behind that scary red door at the end of Wednesday's episode when Titus Andover's thugs dragged Miles through it. (SPOILERS if you're not caught up!)


text:
Carter Taylor Seaton
April 2014
It's the 1960s. The Vietnam War is raging and protests are erupting across the United States. In many quarters, young people are dropping out of society, leaving their urban homes behind in an attempt to find a safe place to live on their own terms, to grow their own food, and to avoid a war they passionately decry. During this time, West Virginia becomes a haven for thousands of these homesteaders—or back-to-the-landers, as they are termed by some. Others call them hippies.
When the going got rough, many left. But a significant number remain to this day. Some were artisans when they arrived, while others adopted a craft that provided them with the cash necessary to survive. Hippie Homesteaders tells the story of this movement from the viewpoint of forty artisans and musicians who came to the state, lived on the land, and created successful careers with their craft. There's the couple that made baskets coveted by the Smithsonian Institution's Renwick Gallery. There's the draft-dodger that fled to Canada and then became a premier furniture maker. There's the Boston-born VISTA worker who started a quilting cooperative. And, there's the immigrant Chinese potter who lived on a commune.
Along with these stories, Hippie Homesteaders examines the serendipitous timing of this influx and the community and economic support these crafters received from residents and state agencies in West Virginia. Without these young transplants, it's possible there would be no Tamarack: The Best of West Virginia, the first statewide collection of fine arts and handcrafts in the nation, and no Mountain Stage, the weekly live musical program broadcast worldwide on National Public Radio since 1983.
Forget what you know about West Virginia. Hippie Homesteaders isn't about coal or hillbillies or moonshine or poverty. It is the story of why West Virginia was—and still is—a kind of heaven to so many.
Carter Taylor Seaton is the author of two novels, Father's Troubles and amo, amas, amat . . . an unconventional love story, numerous magazine articles, and several essays and short stories. She has directed a rural craft cooperative, was nominated for the Ladies Home Journal "Women of the Year 1975" award, and ran three marathons—Atlanta, New York City, and Marine Corps—after she was fifty. A ceramic sculptor living in Huntington, West Virginia, she is a 2013 Tamarack Foundation Fellowship winner, an award given to artisans in recognition of their lifelong achievement in the arts.
"Outstanding. This story has come nowhere near being told with such depth and breadth until now."
"A distinctive story. Engaging. Fascinating."
"You don't have to be born here to be a West Virginian.
"West Virginia takes people in and makes them her own, as truly her children as any in the history of the state.
"And that is what this book is about—about people who came to West Virginia in the late sixties and early seventies, people who came as pilgrims come—to a warm hearth, seeking the companionship of welcoming souls, the safety of the enfolding mountains, and the comfort of friends who encourage growth and creativity.
"Seeking intellectual and political freedom—the same holy grails that created West Virginia in the first place.
"These people did not start out to be West Virginians. In a time when young people were fleeing West Virginia to the clutter, press, and numbing dullness of big city factories, others were arriving. Others, who started out to be something else and the world interfered. They found themselves in a universe not of their creation and not of their liking. Perhaps West Virginia truly was not a part of that universe.
"And so they came.
"Hippies. And they were. On the surface. Young people with their bandanas, T-shirts, ragged shorts, and sandals. Hippies, yes, but, in this book, not hippie caricatures. Carter Taylor Seaton's writing does not allow that. Ms. Seaton knew that to see them only on the surface was a mistake and a disservice to their contributions to the state they chose as home. These were, and are, real people with real depth and real lives, not people easily described and categorized. They are complex, varied, and as multi-talented as any group could possibly be. Using only the talents they brought with them, they sought, and created, riches that are beyond the reach of most of us. These are people who created to live. And, eventually, lived to create.
"These people are West Virginians, who fit within the spirit of West Virginia, and West Virginia should be extremely proud of them.
"And West Virginia should be extremely proud of Carter Taylor Seaton.
"This book was waiting to be written, and perhaps only Ms. Seaton could have written it. Her clear, direct prose is, perhaps, the only prose that is appropriate for this story. Her own creative background and her historian's grasp and presentation of the details, moods, and unrest of the times take the book far beyond a mere description of arts and crafts, providing a marvelous addition to the understanding of what makes West Virginians the people they really are.
"Ms. Seaton's writing, like these people, is a treasure."
Lee Maynard, author of the Crum Trilogy: Crum, Screaming with the Cannibals, and The Scummers and The Pale Light of Sunset


text:
picked up or shipped from our location within 4-6
business days. i
Elio Altare VineyardsElio Altare Vineyards Grandfather Giuseppe Altare purchased the farm and winery in 1948, and our family practiced the typical Piemontese mixed agriculture up until the mid-1970s. Besides winegrapes, the family grew pears. apples, hazelnuts, wheat, and corn. 1971 was the last year in which we worked the land with oxen; after that we gradually acquired tractors and other farm machinery.
Today the family works 10 hectares, of which five are rented. They have adopted techniques aimed at respecting nature. The principle objective is that of limiting the use of chemical substances, both in the vineyard and in the cellar. The wines are not subjected to filtering or fining, so that they keep all of the material and character extracted during maceration.


text:
ANN ARBOR, MI--(Marketwire - Sep 17, 2012) - MichBio, the statewide non-profit, bioscience industry trade association, announced today the launch of MichBio Intel, a new advanced service to help emerging bioscience companies with their product development planning and commercialization efforts. MichBio Intel will enable Michigan's bioscience entrepreneurs and start-up companies to more readily obtain critical, industry-relevant business intelligence on a free or subsidized basis.
The MichBio Intel service is the first in a series of business resources to be offered as part of MichBio Emerging Biz Solutions, a pilot entrepreneurial support services program funded by the Michigan Economic Development Corporation (MEDC).
"MichBio Emerging Biz Solutions is yet another program in Michigan's portfolio of services to support entrepreneurial ventures," said Martin Dober, Senior Vice President of Entrepreneurship & Innovation. "Michigan bioscience companies should benefit greatly from the specialized services that will accelerate their product development and business growth."
The path to market for bioscience companies is a very long one and fraught with numerous business, regulatory and investment funding challenges. Access to supportive resources during the early stages of a company's life cycle can make a huge difference in their likelihood for success.
"Emerging bioscience company entrepreneurs usually focus on the technology aspects of their business, often giving less attention to product development planning, and they don't or can't seek sufficient and relevant professional help to bolster their efforts," said Stephen Rapundalo, PhD, president and CEO of MichBio. "Our new services aim to support companies in these critical areas."
To begin, MichBio Intel will offer access to the following industry databases, and others are likely to be added in the future based on utility and need:
- Life Science Intelligence and MedTech Insight - high-quality global market research across all therapeutic and technology areas, including trends, opportunities, drivers, competitive landscape/market shares, volumes/forecasts, among other information;
- BioPharm Insight and BioPharm Devices - one-stop resources for proprietary forward-looking intelligence, market analytics and decision-making contacts and a comprehensive guide to the medtech industry, including detailed profiles on devices, companies, trials and contacts;
- Innovaro PharmaLicensing - the leading global resource for direct online business development within the life science and biopharmaceutical industries including out- and in-licensing opportunities, tracking and securing technologies, access to the largest business development community, partner searching and contacts, portfolio broadcasting, and other related resources.
MichBio Intel will be available to any emerging bioscience company in Michigan through a qualification process. The program will subsidize costs of information and report delivery based on a company's funding level. In many cases, simple queries will be provided at no cost, while more complex requests will be billed on a fixed rate basis.
Rapundalo added, "Direct access to relevant business intelligence is cost-prohibitive for small companies. They typically don't need full reports at a cost of thousands of dollars but rather selective information. Our MichBio Intel service will be a valuable resource and provide them with the specificity and utility they need."
David McGuire, Director of Membership & Operations at MichBio, will serve as the Project Manager for MichBio Emerging Biz Solutions. All inquiries for services and other information should be directed to email@example.com or (734) 527-9150.
Additional services as part of MichBio Emerging Biz Solutions will be rolled out over the near term. In addition, an independent needs assessment effort is underway to better validate entrepreneurial resource requirements for emerging bioscience companies in Michigan.
MichBio is the trade association committed to driving growth in Michigan's biosciences industry and its many sectors; including AgBio Chemical & Industrial Biotech, Information Technology, Medical Devices Equipment & Supplies, Pharmaceuticals & Therapeutics, Research & Development, and Testing & Medical Laboratories. MichBio members include biosciences companies, academic and research institutions, biosciences service providers, and related organizations. The MichBio Institute is the association's affiliated 501(c)(3) educational foundation. For more information, visit www.michbio.org.


text:
- Home
- Editor's Desk
- Features
- Columns
- Arts & Culture
- Special Editions
- Advertising
Wednesday November 20, 2019; 02:55 pm EST
Take to the Road with Mr. Pish
Move over Lassie, Rin Tin Tin and Benji. Make way for Mr. Pish, the latest rising star in the doggy world.
The feisty 14-year old Jack Russell terrier is a native of Maryland's Eastern Shore. Like most pups, Mr. Pish enjoys swimming, running on the beach, eating, sleeping and petting. But most of all he loves to explore and share his discoveries with his friends. This canine King of the Road is the author of a series of travel books for children, promoting respect for nature and the fun of learning.
Pish's adventures began in 2008 when his person, K.S. Brooks, moved from Maryland's Eastern Shore to Washington State. Brooks and Mr. Pish's driver, David, set up a lookout for Pish in the front seat of their car (The Pishmobile) and his own room in the back. Along the way, Mr. Pish penned postcards to his, fans telling about interesting sites across country.
With guidance from Brooks, a nature photographer, Pish skipped the usual water and amusement parks and sought out natural and historical marvels to wow youngsters. Mr. Pish enjoyed his writing experience so much that he followed up his first book, Postcards from Mr. Pish: A Cross-Country Journal, with five more books, including an East Coast Edition.
Pish tells of his discoveries with all the enthusiasm of a grade schooler: "I got to see where a beaver ate a tree! (Massachusetts), "Cannons and big fort walls … made me want to pretend I was a pirate! (Quebec). "I like New Hampshire very much because it's sort of the shape of my pointy ear."
Along with postcards of city skylines, lighthouses and covered bridges, Mr. Pish sends photos of himself in front of the State House in Annapolis, aboard the Skipjack Nathan in Dorchester, with a Royal Mounty in Regina, Saskatchewan. ("I want to do that when I grow up!") He includes pictures of his encounters with animals like a burrowing owl, a badger and baby wolves that are "cute and cuddly like me!"
Pish shares K. S. Brooks' belief that "Seeing children and young adults respond to knowledge — watching that light go off in their heads — that's what it's all about. Learning should be fun." Teachers use his books in classrooms in the U.S., Mexico and Scotland.
You might expect Mr. Pish to be dog-tired after visiting 41 states and seven Canadian provinces, but he's ready to hit the road again for his next adventure — and his next book.
The Postcards from Mr. Pish series (www.mrpish.comis) is published by Cambridge Books, and the books are available at www.amazon.com. Each purchase benefits Arbor Day Foundation's Nature Explore Program.


text:
6 Rod Holder Jet Ski Fishing Rack with Side Plates for Rotopax Fuel Packs
Model 6930
Universal Jet ski fishing rack made of high quality marine grade stainless steel and then professionally powder coated in flat black and includes all the necessary straps for an easy installation.
Our jet ski fishing rack includes 6 Rod Holders and individual adjustable legs to fit any surface or platform your personal watercraft may have. No drilling or modifications is needed to your personal watercraft and installs in a couple of minutes.
Our jet ski fishing racks are designed fit virtually any jet ski on the market today like Yamaha, Sea Doo, Honda, Kawasaki and Polaris watercraft. Excludes any models with suspension features.
Model 6930
Our 6930 Package Includes:
1- Flat Black Powder Coated Stainless Steel Cooler Rack
6- Stainless Steel Welded Rod Holders with Rod Straps
4 Rod Holders Vertical & 2 Rod Holders Angled
2- Brackets with holes drilled for Rotopax fuel packs
2- 36" Industrial Strength 1" Wide Velcro Straps
1- 6.25"Industrial Strength 1" Velcro Safety Strap
2- 6' Adjustable Cam Style Tie Down Strap
1- 40" Black Bungee Cord for the Cooler
Rack Size: Designed to fit most coolers up to 54 quarts and typically including Yeti 45 and Icey Tek/Frostbite 48 Roto mold coolers
Outside Dimensions: 27.5" X 17.5" X 13.25" top bar - rod holder height not included
Inside Dimensions: 26" X 16" X 12.5" top bar - rod holder height not included
Deck Space: 18" L X 10 1/2" W for the feet
When measuring coolers for our racks - measure the height to only halfway up (12.5 inches) the cooler to get a more accuracy.
MSRP: 489.00 Margin 30%


text:
Vox Voices - female Vocal Group
3rd session on the summer course.
Vox Voices female vocal group is set up as a course of friendly, playful vocal workshops, with body percussion, movement and uplifting world, folk and jazz melodies. Everything is learnt by ear and we weave a tapestry of sound moving from part to part. Vox Voices is for all female singers and would-be singers, regardless of experience, or talent!
Book the whole season of 9 weeks at a discount price or drop in week by week (please email in advance to check there is space!)


text:
Newport, PA by Brooks
If you love traditional European salume, charcuterie, cured meats, air-dried meats, or artisan meats by any other name, then you have come to the right place!
North Mountain Pastures is me – Brooks, my wife Anna, and our two children, Kaj and Leila. We've been farming together on leased land for over 6 years now, and just bought our own land in the spring of 2011.
We are farmers. This means we work to produce a valuable product (meat) from the sun's energy (grass).
Growing the highest quality food possible is our goal. We believe that good food is nutritious and delicious. We believe the way to raise livestock is to provide an environment that mimics nature and is integrated with our natural environment. Our animals thrive naturally: outdoors, eating organic pasture and grains, feeling sun and rain and even snow, clean water, plenty of space. What is good for the animals is good for our farm – the soil is fertilized with manure and protected from erosion with permanent pasture.
The Mission
Throughout pig-raising history, people have known how to apply salt to preserve meat for when they needed it before there were refrigerators, freezers, preservatives, or irradiation. Our predecessors also knew that salted pork is no longer just pork, but becomes a new wonderful creation. There are countless ways to cure meat, from countless traditions around the world. Our mission is to continue to learn how to make the most delicious cured meats, make them from naturally raised livestock, and bring them directly to the people who will be eating it – maybe you!
We want to use this simple process of salt preservation in three ways:
- In an economically and environmentally forward thinking way – Preserving meat with salt rather than refrigeration moves us into the future of necessary lower energy use.
- To preserve knowledge and culture. Naturally curing meat can help us recapture traditional delicacies to thrill modern tastes.
- By adding value to a farm-raised commodity, we set an example for other farmers. The growing common vision of a food system made up of many small, diversified, local farms can become reality when farmers are able to make a living off the land again.
We ask for money to help launch an on-farm, naturally raised and processed cured meat business –one of the first of this kind in the country. Help us create a food culture marked by quality and integrity.
The Project
So here is where you come in! We've been making salumi small-scale in a rented facility off-farm. But we have already begun construction on a cured meat processing facility on our own farm! We need your help to finish the shop, and build our aging room.
Earth-banked Aging Room
Earth-banked means built into a hillside, kind of like a cave, using the natural, constant earth temperature to create a stable environment for drying. This will be a large room where prosciutto hams, bacon, and salamis are hung from the ceiling to properly age and dry. It will be a carefully planned, controlled environment that will age the meats to finest flavor.
By making it earth banked, the aging room will require lower energy use and operating costs. That means dry-cured, artisan meats with a tiny carbon footprint!
This cave will be excavated from the back of the shop, directly accessible from the inside of the butcher shop. Pictures to come soon!
Federal Inspection
I (Brooks) took a class for HACCP (food safety) training last spring, and so am certified to write the plans we need to produce these delicious meats under full federal inspection. Once we have our facilities completed and up to USDA inspection standards, we can sell salumi at farmer's markets, to restaurants, and ship it anywhere in the country!
The Farm
We spent years looking for the right piece of land to call our home. In the meantime, we leased farmland and started our business. We looked at land from southern California to Maine, mostly focusing on Pennsylvania farms, and suddenly, providence! An 85-acre farmstead came up for sale a mile away from our rented farm. We have been here since spring 2011.
We are GRASS farmers first. We don't want to till the land; our goal is to selectively provide access to permanent pasture throughout the seasons. The farm itself is mostly south facing, grassy hills, mixed in with hardwood forest. That means we have lots of nuts! Acorns, hickories, and walnuts provide some food for our pigs as we rotate them through our forest paddocks.
We have a 'farrow to finish' operation, which means our piglets are born here, raised here, and then turned into prosciutto here!
We have been raising pigs for over three years now, and I (Brooks) started curing pork after our first pig was butchered. Our sows are heritage breed pigs, including Tamworth, Berkshire, Yorkshire, and English Black. They farrow (have babies) outdoors or on deep bedding in a sheltered barn, and teach their piglets the virtues of grazing and rooting at an early age. We supplement their pasture diet with organically-grown grains produced in Pennsylvania, and occasionally they also get fresh whey from organic cheese-making operations. This means we have no GMO grains, no antibiotics, and nothing unnatural in our pork. Its delicious!
We are now butchering about 50 pigs a year, and curing meat in a rented facility. With your help, we will be completing a small sausage and salume shop on our farm, and building an earth-banked aging room
The Product
With the slaughter of livestock comes great responsibility – we believe their blood is on our hands and must be put to appropriate end use. To give homage to the lives and deaths of our animals, we want to express the utmost appreciation and gratitude for the gift of good meat. Beautiful Bacon.
- We preserve purity: by using only100% natural, organic spices and unrefined sea salt. Good meats are marked by nutrient-dense richness,
- We preserve with old-world traditional skill: by careful hand butchering and processing. Truly high quality meats are artisanal and growing rare.
- We preserve and enhance the natural flavors by fermentation alchemy. Like grapes into wine, and milk into cheese, pork into prosciutto is simply magical. Salami and the like have unparalleled ability to satisfy the appetite and delight the senses,
Salumi are created with pork and salt (hence the sal- prefix on salami and salumi). In our case, because we raise our pigs organically, we use only the finest – Himalayan sea salt, and organic herbs and spices – no nitrites!
For whole muscle salume like prosciutto, the salt is rubbed directly on the meat with any herbs or spices, and then kept in cold storage (refrigerated) for a week to a month, depending on the size of the cut. Then it is moved to a drying room to age completely, anywhere from a month to 2 years!
Salami is a fermented sausage that is salted, spiced, ground and stuffed into a casing with a bacterial culture. We keep the sausage at a warm temperature for the duration of its fermentation (a day or two), then transfer to the drying room.


text:
TOPEKA, Kan. (December 20, 2012) – In its final meeting of 2012, the Sports Car Club of America Board of Directors named Lisa Noble, of Manhattan, Kan., as its chairman for 2013. The Board also approved appointments for various committees and board liaisons that will oversee the organization in the coming calendar year.
Noble is a long-time Kansas Region SCCA member and begins her sixth year as the Area Six Director. As a Formula Vee driver, Noble was the first female in the class to earn a National race win, in 1995 at Heartland Park Topeka. Noble was also the 1999 Kimberly Cup award winner for most improved driver. She has spent the past three years serving as Vice Chairman of the Board of Directors.
"This is an incredible time to be a part of SCCA," Noble said. "We are presented with tremendous opportunities in 2013 to advance our Club into the future while also celebrating its heritage. I thank my fellow Board members for their confidence and look forward to serving the Club as Chairman."
The torch passes to Noble from outgoing Chairman Jerry Wannarka, who did not seek reappointment for a fourth year as Chairman.
Also selected as Board Officers are Dick Patullo (Vice Chairman), Todd Butler (Secretary), Michael Lewis (Treasurer), John Walsh (Assistant Treasurer) and Jerry Wannarka (Fifth Member Executive Committee).
Board committees and liaisons were also appointed. Those are as follows:
Budget and Finance:
Michael Lewis, Chair
Bill Kephart
Brian McCarthy
Phil Creighton
John Walsh
Compensation Committee:
Dick Patullo, Chair
Steve Harris
Michael Lewis
Bill Kephart
R. David Jones
Jerry Wannarka
Planning Committee:
Bill Kephart, Chair
Todd Butler, Secretary
John Walsh
Robin Langlotz
Michael Lewis
Steve Harris
SCCA Board Liaisons:
Solo Event Board: Phil Creighton/Bruce Lindstrand
Club Racing Board: Todd Butler/John Walsh
TTAC: John Walsh/Brian McCarthy
Stewards: Bill Kephart
Court of Appeals: John Walsh
REs & Operations Liaison: R. David Jones/Jerry Wannarka
RoadRally Board: R. David Jones/Bruce Lindstrand
RallyCross Board: Brian McCarthy/ Steve Harris
SCCA Foundation: Jerry Wannarka
SCCA Enterprises Board: Jerry Wannarka
SCCA Pro Racing: Lisa Noble


text:
Why a Maine GOP senator is taking on oil speculators
WASHINGTON — Oil prices shot past $72 a barrel this week, and a growing number of experts point to Wall Street speculators as a key reason why Americans are suddenly paying a lot more for oil and gasoline.
Although soaring oil prices threaten the fragile economic recovery, most Capitol Hill lawmakers have remained silent about them, but not Sen. Susan Collins. The Maine Republican pumps her own gas and heats her Bangor home with oil, and on trips home, she gets an earful from angry consumers, who, like her, blame speculators.
"Constituents get it," she said. "They don't see the reason for it. They don't see (supply) shortages. They don't see (the Organization of Petroleum Exporting Countries) greatly reducing production or other reasons prices are going up so much."
Collins has been one of the few on Capitol Hill and even fewer Republicans who blame the rising oil prices in part on Wall Street investors. She and her allies, mostly Democrats, are trying to limit speculative investments in oil and other commodities, but they say they need more support from President Barack Obama.
McClatchy has been reporting for 14 months that speculative investment — not simply supply and demand — has been helping drive oil prices higher. On June 5, Deutsche Bank, a major global financial institution, echoed the warning that excessive speculation is behind rising oil and gasoline prices. The price of crude peaked nearly a year ago at $147 a barrel.
"Crude oil prices appear to have been divorced from the underlying fundamentals of weak demand, ample supply, and high inventories," commodity analysts at the bank wrote. "Rising OPEC production and very high OPEC spare capacity also appear to be unimportant to oil market investors at this time."
OPEC on Friday further reduced, by 1.62 million barrels per day, its forecast for global oil consumption this year.
Yet a weaker dollar and bullish views on a global economic recovery are driving speculators back into the oil market, prompting a new wave of speculative investment from non-commercial traders — those who don't actually use the product — into contracts for future delivery of oil.
"We believe the current oil market environment bears a striking resemblance to the 2005-2008 environment, both of which are characterized, in our view, by fundamentally unjustifiable price increases," said Mark Gilman, a veteran oil analyst for investment manager The Benchmark Company, who added that oil should cost about $50 a barrel.
Exactly, Collins said.
"You can't have this enormous influx of non-commercial trades and so much money and think that it doesn't affect the price," she said.
Speculators such as Gresham Investment Management, a company that invests in commodities on behalf of big players such as pension funds and endowments, disagree.
"Speculators simply bet on the price of an underlying commodity, and because they control neither the supply of nor the demand for the commodity, they leave themselves open to the vagaries of the market," said Jason Ungar, Gresham's managing director, who fears that Collins and others will set limits on oil trading.
Collins, 56, hails from Caribou, the northern Maine city of about 8,000 people where her family has owned a lumber company for five generations. Both her parents were mayors of Caribou, and Collins has a long career in public service, first as a U.S. Senate staffer and later as a state regulator before she was elected senator in 1996.
She returns home every weekend. Asked if she knows the current price, she quickly shot back, "You bet I do!"
Some 80 percent of the homes in Maine are heated with oil, and rising gasoline prices hurt Maine's tourist industry. Collins regularly meets truckers, shop owners and retirees across the state who fret about rising energy costs. Elderly constituents tell her that they expect to move in with their children if home heating prices keep rising.
"She gets it," said Mark Brewer, an assistant professor of political science at the University of Maine.
That was evident earlier this month when the mild-mannered Collins grilled Gary Gensler, the recently confirmed chairman of the Commodity Futures Trading Commission.
"I understand that those investors' intention is to provide good returns as a hedge against inflation, asset diversification," she said. "But the effect of that activity cumulatively appears to drive up the price for some of the traditional users of the commodity markets."
Her view that speculative investing is behind rising prices, she suggested, came from ordinary folks, adding that, "Just a week ago, Maine's fuel dealers were in my office saying that they believe excessive speculation by non-commercial players is once again driving up the cost of oil."
Collins supports the Obama administration's recent call for limits on how much trading of oil contracts can be done by those who aren't users or producers of oil. She also wants to close a loophole that allows big Wall Street firms such as Goldman Sachs and others to avoid limits on how many oil contracts they can trade.
That's important, said hedge fund manager Michael Masters, because speculative investors now dominate the oil markets. A decade ago, speculators controlled about 25 percent of all overnight holdings of oil contracts, but today they control about 80 percent.
"It's completely reversed," said Masters, who testifies frequently before Congress.
Collins isn't alone in her quest. Sen. Bernard Sanders, an Independent from Vermont, held up Gensler's nomination for two months before he got assurances that the Obama administration would consider more regulation of commodities markets.
Senate Agriculture Committee Chairman Tom Harkin, D-Iowa., hopes to pass new commodities regulations by fall, and these efforts are supported Sen. Carl Levin, D-Mich., the chairman of the Permanent Subcommittee on Investigations.
The Maine Republican has another distinction, however: She's one of only three Republican U.S. senators from the Northeast — the others are Olympia Snowe of Maine and Judd Gregg of New Hampshire — and she's a rare Republican who favors further government regulation of oil trading.
It's why she thinks that ultimately changes may come only when Obama gets personally involved.
"He can help us explain why it matters," Collins said.
Collins was also one of only three Republicans voting in favor of Obama's economic stimulus bill, and that only after getting a promise of spending cuts and more school construction. Obama may need a handful of Republicans to revamp of financial regulation, including the oil markets.
Will Collins demand something again from the president in exchange for crossing party lines?
Offering a coy smile, Collins answered, "I do think the president has been receptive when I make suggestions."
Glossary of Wall St. intersection with oil
To ask a question about this story or any economic question, go to McClatchy's economy Q&A
Are Wall Street speculators driving up gasoline prices?
Senate approves FDA regulation of tobacco products
Some rejected Chrysler dealers turn to used cars
How much clout will labor have in health care debate?
Follow the latest politics news at McClatchy's