Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6

* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6:
  [S390] dasd: tunable missing interrupt handler
  [S390] dasd: allocate fallback cqr for reserve/release
  [S390] topology: use default MC domain initializer
  [S390] initrd: change default load address
  [S390] cmm, smsgiucv_app: convert sender to uppercase
  [S390] cmm: add missing __init/__exit annotations
  [S390] cio: use all available paths for some internal I/O
  [S390] ccwreq: add ability to use all paths
  [S390] cio: ccw_device_online_store return -EINVAL in case of missing driver
  [S390] cio: Log the response from the unit check handler
  [S390] cio: CHSC SIOSL Support
This commit is contained in:
Linus Torvalds 2010-08-10 14:01:26 -07:00
commit 0d6ffdb8f1
17 changed files with 275 additions and 38 deletions

View File

@ -208,6 +208,8 @@ extern void ccw_device_get_id(struct ccw_device *, struct ccw_dev_id *);
extern struct ccw_device *ccw_device_probe_console(void);
extern int ccw_device_force_console(void);
int ccw_device_siosl(struct ccw_device *);
// FIXME: these have to go
extern int _ccw_device_get_subchannel_number(struct ccw_device *);

View File

@ -30,8 +30,6 @@ static inline void s390_init_cpu_topology(void)
};
#endif
#define SD_MC_INIT SD_CPU_INIT
#include <asm-generic/topology.h>
#endif /* _ASM_S390_TOPOLOGY_H */

View File

@ -366,7 +366,7 @@ iplstart:
l %r1,.Lstartup
br %r1
.Linitrd:.long _end + 0x400000 # default address of initrd
.Linitrd:.long _end # default address of initrd
.Lparm: .long PARMAREA
.Lstartup: .long startup
.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40

View File

@ -427,7 +427,7 @@ static struct notifier_block cmm_power_notifier = {
.notifier_call = cmm_power_event,
};
static int cmm_init(void)
static int __init cmm_init(void)
{
int rc = -ENOMEM;
@ -435,6 +435,13 @@ static int cmm_init(void)
if (!cmm_sysctl_header)
goto out_sysctl;
#ifdef CONFIG_CMM_IUCV
/* convert sender to uppercase characters */
if (sender) {
int len = strlen(sender);
while (len--)
sender[len] = toupper(sender[len]);
}
rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
if (rc < 0)
goto out_smsg;
@ -467,7 +474,7 @@ out_sysctl:
}
module_init(cmm_init);
static void cmm_exit(void)
static void __exit cmm_exit(void)
{
unregister_sysctl_table(cmm_sysctl_header);
#ifdef CONFIG_CMM_IUCV

View File

@ -1083,6 +1083,49 @@ dasd_eer_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
/*
* expiration time for default requests
*/
static ssize_t
dasd_expires_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dasd_device *device;
int len;
device = dasd_device_from_cdev(to_ccwdev(dev));
if (IS_ERR(device))
return -ENODEV;
len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_expires);
dasd_put_device(device);
return len;
}
static ssize_t
dasd_expires_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dasd_device *device;
unsigned long val;
device = dasd_device_from_cdev(to_ccwdev(dev));
if (IS_ERR(device))
return -ENODEV;
if ((strict_strtoul(buf, 10, &val) != 0) ||
(val > DASD_EXPIRES_MAX) || val == 0) {
dasd_put_device(device);
return -EINVAL;
}
if (val)
device->default_expires = val;
dasd_put_device(device);
return count;
}
static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store);
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
@ -1094,6 +1137,7 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_eer_enabled.attr,
&dev_attr_erplog.attr,
&dev_attr_failfast.attr,
&dev_attr_expires.attr,
NULL,
};

View File

@ -43,7 +43,7 @@ MODULE_LICENSE("GPL");
sizeof(struct dasd_diag_req)) / \
sizeof(struct dasd_diag_bio)) / 2)
#define DIAG_MAX_RETRIES 32
#define DIAG_TIMEOUT 50 * HZ
#define DIAG_TIMEOUT 50
static struct dasd_discipline dasd_diag_discipline;
@ -360,6 +360,8 @@ dasd_diag_check_device(struct dasd_device *device)
goto out;
}
device->default_expires = DIAG_TIMEOUT;
/* Figure out position of label block */
switch (private->rdc_data.vdev_class) {
case DEV_CLASS_FBA:
@ -563,7 +565,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
cqr->expires = DIAG_TIMEOUT;
cqr->expires = memdev->default_expires * HZ;
cqr->status = DASD_CQR_FILLED;
return cqr;
}

View File

@ -82,6 +82,14 @@ static struct ccw_driver dasd_eckd_driver; /* see below */
#define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2
/* emergency request for reserve/release */
static struct {
struct dasd_ccw_req cqr;
struct ccw1 ccw;
char data[32];
} *dasd_reserve_req;
static DEFINE_MUTEX(dasd_reserve_mutex);
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
@ -1107,8 +1115,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
struct dasd_eckd_private *private;
struct dasd_block *block;
struct dasd_uid temp_uid;
int is_known, rc;
int is_known, rc, i;
int readonly;
unsigned long value;
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev,
@ -1143,6 +1152,18 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err1;
/* set default timeout */
device->default_expires = DASD_EXPIRES;
if (private->gneq) {
value = 1;
for (i = 0; i < private->gneq->timeout.value; i++)
value = 10 * value;
value = value * private->gneq->timeout.number;
/* do not accept useless values */
if (value != 0 && value <= DASD_EXPIRES_MAX)
device->default_expires = value;
}
/* Generate device unique id */
rc = dasd_eckd_generate_uid(device);
if (rc)
@ -1973,7 +1994,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@ -2150,7 +2171,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@ -2398,7 +2419,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
@ -2645,15 +2666,23 @@ dasd_eckd_release(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate initialization request");
return PTR_ERR(cqr);
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
cqr = &dasd_reserve_req->cqr;
memset(cqr, 0, sizeof(*cqr));
memset(&dasd_reserve_req->ccw, 0,
sizeof(dasd_reserve_req->ccw));
cqr->cpaddr = &dasd_reserve_req->ccw;
cqr->data = &dasd_reserve_req->data;
cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RELEASE;
@ -2671,7 +2700,10 @@ dasd_eckd_release(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
dasd_sfree_request(cqr, cqr->memdev);
if (useglobal)
mutex_unlock(&dasd_reserve_mutex);
else
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@ -2687,15 +2719,23 @@ dasd_eckd_reserve(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate initialization request");
return PTR_ERR(cqr);
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
cqr = &dasd_reserve_req->cqr;
memset(cqr, 0, sizeof(*cqr));
memset(&dasd_reserve_req->ccw, 0,
sizeof(dasd_reserve_req->ccw));
cqr->cpaddr = &dasd_reserve_req->ccw;
cqr->data = &dasd_reserve_req->data;
cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RESERVE;
@ -2713,7 +2753,10 @@ dasd_eckd_reserve(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
dasd_sfree_request(cqr, cqr->memdev);
if (useglobal)
mutex_unlock(&dasd_reserve_mutex);
else
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@ -2728,15 +2771,23 @@ dasd_eckd_steal_lock(struct dasd_device *device)
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Could not allocate initialization request");
return PTR_ERR(cqr);
mutex_lock(&dasd_reserve_mutex);
useglobal = 1;
cqr = &dasd_reserve_req->cqr;
memset(cqr, 0, sizeof(*cqr));
memset(&dasd_reserve_req->ccw, 0,
sizeof(dasd_reserve_req->ccw));
cqr->cpaddr = &dasd_reserve_req->ccw;
cqr->data = &dasd_reserve_req->data;
cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_SLCK;
@ -2754,7 +2805,10 @@ dasd_eckd_steal_lock(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
dasd_sfree_request(cqr, cqr->memdev);
if (useglobal)
mutex_unlock(&dasd_reserve_mutex);
else
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@ -3488,10 +3542,15 @@ dasd_eckd_init(void)
int ret;
ASCEBC(dasd_eckd_discipline.ebcname, 4);
dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
GFP_KERNEL | GFP_DMA);
if (!dasd_reserve_req)
return -ENOMEM;
ret = ccw_driver_register(&dasd_eckd_driver);
if (!ret)
wait_for_device_probe();
else
kfree(dasd_reserve_req);
return ret;
}
@ -3499,6 +3558,7 @@ static void __exit
dasd_eckd_cleanup(void)
{
ccw_driver_unregister(&dasd_eckd_driver);
kfree(dasd_reserve_req);
}
module_init(dasd_eckd_init);

View File

@ -320,7 +320,12 @@ struct dasd_gneq {
__u8 identifier:2;
__u8 reserved:6;
} __attribute__ ((packed)) flags;
__u8 reserved[7];
__u8 reserved[5];
struct {
__u8 value:2;
__u8 number:6;
} __attribute__ ((packed)) timeout;
__u8 reserved3;
__u16 subsystemID;
__u8 reserved2[22];
} __attribute__ ((packed));

View File

@ -163,6 +163,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
return rc;
}
device->default_expires = DASD_EXPIRES;
readonly = dasd_device_is_ro(device);
if (readonly)
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
@ -370,7 +372,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */
cqr->retries = 32;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;

View File

@ -186,7 +186,7 @@ struct dasd_ccw_req {
/* ... and how */
unsigned long starttime; /* jiffies time of request start */
int expires; /* expiration period in jiffies */
unsigned long expires; /* expiration period in jiffies */
char lpm; /* logical path mask */
void *data; /* pointer to data area */
@ -224,6 +224,9 @@ struct dasd_ccw_req {
#define DASD_CQR_CLEARED 0x84 /* request was cleared */
#define DASD_CQR_SUCCESS 0x85 /* request was successful */
/* default expiration time*/
#define DASD_EXPIRES 300
#define DASD_EXPIRES_MAX 40000000
/* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
@ -404,6 +407,9 @@ struct dasd_device {
/* hook for alias management */
struct list_head alias_list;
/* default expiration time in s */
unsigned long default_expires;
};
struct dasd_block {

View File

@ -38,9 +38,13 @@ static u16 ccwreq_next_path(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
if (!req->singlepath) {
req->mask = 0;
goto out;
}
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask >>= 1, req->lpm);
out:
return req->mask;
}
@ -113,8 +117,12 @@ void ccw_request_start(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
/* Try all paths twice to counter link flapping. */
req->mask = 0x8080;
if (req->singlepath) {
/* Try all paths twice to counter link flapping. */
req->mask = 0x8080;
} else
req->mask = req->lpm;
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask, req->lpm);
req->drc = 0;
@ -182,6 +190,8 @@ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
/* Ask the driver what to do */
if (cdev->drv && cdev->drv->uc_handler) {
todo = cdev->drv->uc_handler(cdev, lcirb);
CIO_TRACE_EVENT(2, "uc_response");
CIO_HEX_EVENT(2, &todo, sizeof(todo));
switch (todo) {
case UC_TODO_RETRY:
return IO_STATUS_ERROR;

View File

@ -29,6 +29,7 @@
#include "chsc.h"
static void *sei_page;
static DEFINE_SPINLOCK(siosl_lock);
static DEFINE_SPINLOCK(sda_lock);
/**
@ -48,6 +49,7 @@ int chsc_error_from_response(int response)
case 0x0007:
case 0x0008:
case 0x000a:
case 0x0104:
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
@ -974,3 +976,49 @@ int chsc_sstpi(void *page, void *result, size_t size)
return (rr->response.code == 0x0001) ? 0 : -EIO;
}
static struct {
struct chsc_header request;
u32 word1;
struct subchannel_id sid;
u32 word3;
struct chsc_header response;
u32 word[11];
} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE)));
int chsc_siosl(struct subchannel_id schid)
{
unsigned long flags;
int ccode;
int rc;
spin_lock_irqsave(&siosl_lock, flags);
memset(&siosl_area, 0, sizeof(siosl_area));
siosl_area.request.length = 0x0010;
siosl_area.request.code = 0x0046;
siosl_area.word1 = 0x80000000;
siosl_area.sid = schid;
ccode = chsc(&siosl_area);
if (ccode > 0) {
if (ccode == 3)
rc = -ENODEV;
else
rc = -EBUSY;
CIO_MSG_EVENT(2, "chsc: chsc failed for 0.%x.%04x (ccode=%d)\n",
schid.ssid, schid.sch_no, ccode);
goto out;
}
rc = chsc_error_from_response(siosl_area.response.code);
if (rc)
CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n",
schid.ssid, schid.sch_no,
siosl_area.response.code);
else
CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n",
schid.ssid, schid.sch_no);
out:
spin_unlock_irqrestore(&siosl_lock, flags);
return rc;
}
EXPORT_SYMBOL_GPL(chsc_siosl);

View File

@ -80,4 +80,6 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp);
int chsc_error_from_response(int response);
int chsc_siosl(struct subchannel_id schid);
#endif

View File

@ -36,6 +36,7 @@
#include "ioasm.h"
#include "io_sch.h"
#include "blacklist.h"
#include "chsc.h"
static struct timer_list recovery_timer;
static DEFINE_SPINLOCK(recovery_lock);
@ -486,9 +487,11 @@ static int online_store_handle_offline(struct ccw_device *cdev)
spin_lock_irq(cdev->ccwlock);
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
spin_unlock_irq(cdev->ccwlock);
} else if (cdev->online && cdev->drv && cdev->drv->set_offline)
return 0;
}
if (cdev->drv && cdev->drv->set_offline)
return ccw_device_set_offline(cdev);
return 0;
return -EINVAL;
}
static int online_store_recog_and_online(struct ccw_device *cdev)
@ -505,8 +508,8 @@ static int online_store_recog_and_online(struct ccw_device *cdev)
return -EAGAIN;
}
if (cdev->drv && cdev->drv->set_online)
ccw_device_set_online(cdev);
return 0;
return ccw_device_set_online(cdev);
return -EINVAL;
}
static int online_store_handle_online(struct ccw_device *cdev, int force)
@ -598,6 +601,25 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf)
}
}
static ssize_t
initiate_logging(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct subchannel *sch = to_subchannel(dev);
int rc;
rc = chsc_siosl(sch->schid);
if (rc < 0) {
pr_warning("Logging for subchannel 0.%x.%04x failed with "
"errno=%d\n",
sch->schid.ssid, sch->schid.sch_no, rc);
return rc;
}
pr_notice("Logging for subchannel 0.%x.%04x was triggered\n",
sch->schid.ssid, sch->schid.sch_no);
return count;
}
static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
@ -605,10 +627,12 @@ static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
static DEVICE_ATTR(logging, 0200, NULL, initiate_logging);
static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr,
&dev_attr_pimpampom.attr,
&dev_attr_logging.attr,
NULL,
};
@ -2036,6 +2060,21 @@ void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
}
}
/**
* ccw_device_siosl() - initiate logging
* @cdev: ccw device
*
* This function is used to invoke model-dependent logging within the channel
* subsystem.
*/
int ccw_device_siosl(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
return chsc_siosl(sch->schid);
}
EXPORT_SYMBOL_GPL(ccw_device_siosl);
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);

View File

@ -208,6 +208,7 @@ static void spid_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
req->singlepath = 1;
req->callback = spid_callback;
spid_do(cdev);
}
@ -420,6 +421,7 @@ static void verify_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
req->singlepath = 1;
if (cdev->private->flags.pgroup) {
CIO_TRACE_EVENT(4, "snid");
CIO_HEX_EVENT(4, devid, sizeof(*devid));
@ -507,6 +509,7 @@ void ccw_device_disband_start(struct ccw_device *cdev)
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = sch->schib.pmcw.pam & sch->opm;
req->singlepath = 1;
req->callback = disband_callback;
fn = SPID_FUNC_DISBAND;
if (cdev->private->flags.mpath)

View File

@ -92,11 +92,12 @@ enum io_status {
* @filter: optional callback to adjust request status based on IRB data
* @callback: final callback
* @data: user-defined pointer passed to all callbacks
* @singlepath: if set, use only one path from @lpm per start I/O
* @cancel: non-zero if request was cancelled
* @done: non-zero if request was finished
* @mask: current path mask
* @retries: current number of retries
* @drc: delayed return code
* @cancel: non-zero if request was cancelled
* @done: non-zero if request was finished
*/
struct ccw_request {
struct ccw1 *cp;
@ -108,12 +109,13 @@ struct ccw_request {
enum io_status);
void (*callback)(struct ccw_device *, void *, int);
void *data;
unsigned int singlepath:1;
/* These fields are used internally. */
unsigned int cancel:1;
unsigned int done:1;
u16 mask;
u16 retries;
int drc;
int cancel:1;
int done:1;
} __attribute__((packed));
/*

View File

@ -180,6 +180,13 @@ static int __init smsgiucv_app_init(void)
goto fail_put_driver;
}
/* convert sender to uppercase characters */
if (sender) {
int len = strlen(sender);
while (len--)
sender[len] = toupper(sender[len]);
}
/* register with the smsgiucv device driver */
rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
if (rc) {